Spring Cloud

Eureka

Eureka基础知识

服务治理:Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务治理;在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

服务注册与发现:Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址)

Spring Cloud - 图2

Eureka的两个组件

Eureka Server提供服务注册服务

各个微服务节点通过配置启动后,会在Eureka Server中进行注册, 这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

Eureka Client通过注册中心进行访问

是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备个内置的、 使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。 如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)

单机Eureka构建步骤

一、IDEA生成EurekaServer端服务注册中心 类似物业公司

建立Module:cloud-eureka-server7001

改pom:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5. </dependency>
  6. </dependencies>

写yml:

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: cloud-eureka-service
  6. eureka:
  7. instance:
  8. # eureka服务端的实例名称
  9. # 单机 hostname: localhost
  10. hostname: eureka7001.com
  11. client:
  12. # false表示不向注册中心注册自己
  13. register-with-eureka: false
  14. # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
  15. fetch-registry: false
  16. service-url:
  17. # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
  18. # 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  19. defaultZone: http://eureka7001.com:7001/eureka

主启动:

  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class EurekaMain7001 {
  4. public static void main(String[] args){
  5. SpringApplication.run(EurekaMain7001.class,args);
  6. }
  7. }

二、EurekaClient端cloud-provider-payment8001 将注册进EurekaServer成为服务提供者provider,类似于尚硅谷学校对外提供授课服务

三、EurekaClient端cloud-consumer-order80 将注册进EurekaServer成为服务消费者consumer,类似于学校上课消费的各位同学

建Module:cloud-provider-payment8001

改pom:

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  4. </dependency>

写yml:

  1. server:
  2. port: 8001
  3. spring:
  4. application:
  5. name: cloud-payment-service
  6. datasource:
  7. type: com.alibaba.druid.pool.DruidDataSource
  8. # mysql驱动类
  9. driver-class-name: com.mysql.cj.jdbc.Driver
  10. url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  11. username: root
  12. password: root
  13. eureka:
  14. client:
  15. register-with-eureka: true
  16. fetch-registry: true
  17. service-url:
  18. defaultZone: http://eureka7001.com:7001/eureka
  19. instance:
  20. #在Eureka中显示主机的服务名称
  21. instance-id: payment8001
  22. #左下角显示ip地址;详情可见下图
  23. prefer-ip-address: true
  24. mybatis:
  25. mapper-locations: classpath:mapper/*.xml
  26. type-aliases-package: com.atguigu.springcloud.entities

Spring Cloud - 图3

主启动:

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. public class PaymentMain8001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(PaymentMain8001.class, args);
  6. }
  7. }

集群Eureka构建步骤

容易理解一点:集群就是相互注册,把A服务器注册到B,B服务器也注册到A。

然后服务要注册到这两个server上。

一、Eureka集群原理说明

Spring Cloud - 图4

解决办法: 搭建Eureka注册中心集群,实现负载均衡+故障容错

二、Eureka集群环境构建步骤

建Module:参考cloud-eureka-server7001建立cloud-eureka-server7002

改pom:

修改映射配置:找到hosts文件;添加两行;刷新ipconfig/flushdns

  1. 127.0.0.1 eureka7001.com
  2. 127.0.0.1 eureka7002.com

写yml:

单机时:

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: cloud-eureka-service
  6. eureka:
  7. instance:
  8. # eureka服务端的实例名称
  9. 单机 hostname: localhost
  10. client:
  11. # false表示不向注册中心注册自己
  12. register-with-eureka: false
  13. # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
  14. fetch-registry: false
  15. service-url:
  16. # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
  17. # 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  18. defaultZone: http://eureka7001.com:7001/eureka

集群时:

7001yml

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: cloud-eureka-service
  6. eureka:
  7. instance:
  8. # eureka服务端的实例名称
  9. hostname: eureka7001.com
  10. client:
  11. # false表示不向注册中心注册自己
  12. register-with-eureka: false
  13. # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
  14. fetch-registry: false
  15. service-url:
  16. # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
  17. defaultZone: http://eureka7002.com:7002/eureka/

7002yml

  1. server:
  2. port: 7002
  3. spring:
  4. application:
  5. name: cloud-eureka-service2
  6. eureka:
  7. instance:
  8. hostname: eureka7002.com
  9. client:
  10. register-with-eureka: false
  11. fetch-registry: false
  12. service-url:
  13. defaultZone: http://eureka7001.com:7001/eureka/

主启动:不变

三、将支付服务8001微服务发布到上面两台Eureka集群配置中

  1. eureka:
  2. client:
  3. register-with-eureka: true
  4. fetch-registry: true
  5. service-url:
  6. # 集群版
  7. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  8. #如果只是单机版,是没有http://eureka7002.com:7002/eureka的。

四、将订单微服务发布到上面两台Eureka集群配置中

  1. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

五、测试01

  1. 先要启动EurekaServer,7001/7002服务
  2. 再要启动服务提供者provider,8001
  3. 再要启动消费者,80
  4. http://localhost/consumer/payment/get/31

Spring Cloud - 图5

Spring Cloud - 图6

相互注册,相互守望。

六、支付服务提供者8001集群环境搭建

建Module:参考cloud-provider-payment8001新建8002

改pom:

yml:

  1. server:
  2. port: 8002
  3. spring:
  4. application:
  5. name: cloud-payment-service8002
  6. datasource:
  7. # 当前数据源操作类型
  8. type: com.alibaba.druid.pool.DruidDataSource
  9. # mysql驱动类
  10. driver-class-name: com.mysql.cj.jdbc.Driver
  11. url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  12. username: root
  13. password: root
  14. eureka:
  15. client:
  16. register-with-eureka: true
  17. fetch-registry: true
  18. service-url:
  19. defaultZone: http://eureka7001.com/eureka,http://eureka7002.com/eureka
  20. mybatis:
  21. mapper-locations: classpath*:mapper/*.xml
  22. type-aliases-package: com.atguigu.springcloud.entities

主启动:

业务类:从8001粘贴。

controller:修改8001和8002的controller

  1. @RestController
  2. @Slf4j
  3. public class PaymentController {
  4. @Resource
  5. private PaymentService paymentService;
  6. @Value("${server.port}")
  7. private String serverport;
  8. @Resource
  9. private DiscoveryClient discoveryClient;
  10. @PostMapping("/payment/create")
  11. public CommonResult<Integer> create(@RequestBody Payment payment) {
  12. int result = paymentService.create(payment);
  13. log.info("***插入结果***"+result);
  14. if (result > 0) {
  15. return new CommonResult<>(200, "success serverport: "+serverport, result);
  16. }
  17. else {
  18. return new CommonResult<Integer>(444, "failure", null);
  19. }
  20. }
  21. @GetMapping("/payment/get/{id}")
  22. public CommonResult getPaymentById(@PathVariable("id") Long id) {
  23. Payment payment = paymentService.getPaymentById(id);
  24. log.info("****插入结果****"+payment);
  25. if (payment != null) {
  26. return new CommonResult(200, "success serverport: "+serverport, payment);
  27. }
  28. else {
  29. return new CommonResult(444, "failure", null);
  30. }
  31. }
  32. }

七、负载均衡

订单服务访问地址不能写死:

  1. // 通过在eureka上注册过的微服务名称调用
  2. public static final String PAYMENT_URL="http://CLOUD-PAYMENT-SERVICE";

Spring Cloud - 图7

使用@LoadBalanced注解赋予RestTemplate负载均衡的能力

  1. @Configuration
  2. public class ApplicationContextConfig {
  3. @Bean
  4. @LoadBalanced
  5. public RestTemplate getRestTemplate() {
  6. return new RestTemplate();
  7. }
  8. }

八、测试02

  1. 先启动EurekaServer7001/7002服务
  2. 在要启动服务提供者provider,8001/8002服务
  3. http://localhost/consumer/payment/get/31
  4. 结果:负载均衡效果达到;8001/8002端口交替出现
  5. Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了。

服务发现Discovery

一、对于注册eureka里面的微服务,可以通过服务发现来获得该服务的信息

二、修改cloud-provider-payment8001的Controller

Spring Cloud - 图8

Spring Cloud - 图9

三、8001启动类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @EnableDiscoveryClient
  4. public class PaymentMain8001 {
  5. public static void main(String[] args) {
  6. SpringApplication.run(PaymentMain8001.class, args);
  7. }
  8. }

四、自测

  1. 先启动EurekaServer
  2. 再启动8001主启动类
  3. http://localhost:8001/payment/discovery

Spring Cloud - 图10

Spring Cloud - 图11

Ribbon负载均衡调用

概述

一、是什么

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目, 主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer (简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

二、能做什么

LB(负载均衡)

一句话:负载均衡+RestTemplate调用

Ribbon负载均衡演示

一、架构说明

Spring Cloud - 图12

Ribbon在工作时分成两步:

第一步先选择Eureka Server ,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

总结: Ribbon其实就是一个软负载均衡的客户端组件, 他可以和其他所需请求的客户端结合使用,和eureka结合只是其中一个实例

二、POM

Spring Cloud - 图13

Spring Cloud - 图14

三、二说RestTemplate的使用

getForObject方法/getForEntity方法,同postForObject/postEntity

Spring Cloud - 图15

Spring Cloud - 图16

Ribbon的核心组件IRule

IRule:根据特定算法从服务列表中选取一个要访问的服务

Spring Cloud - 图17

如何替换

修改cloud-consumer-order80

注意配置细节:

Spring Cloud - 图18

Spring Cloud - 图19

Spring Cloud - 图20

新建package:com.atguigu.myrule

建立MySelfRule规则类:

  1. @Configuration
  2. public class MySelfRule {
  3. @Bean
  4. public IRule myRule() {
  5. // 定义为随机
  6. return new RoundRobinRule();
  7. }
  8. }

主启动类上面添加@RibbonClient

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
  4. public class OrderMain80 {
  5. public static void main(String[] args) {
  6. SpringApplication.run(OrderMain80.class, args);
  7. }
  8. }

测试:http://localhost/consumer/payment/get/31

Ribbon负载均衡算法

原理:

Spring Cloud - 图21

OpenFeign服务接口调用

概述

一、openfeign是什么:

Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可

二、能干嘛

Feign旨在使编写Java Http客户端变得更容易。

前面在使用Ribbon+ RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。 但是在实际开发中,由于对服务依赖的调用可能不止一处, 往往一个接口会被多处调用, 所以通常都会针对每个微服务自行封装-些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装, 由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方 式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口. 上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign集成了Ribbon

利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义
服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

三、feign和openfeign区别

Spring Cloud - 图22

openfeign使用步骤

接口+注解:

微服务调用接口+@FeignClient

建Module

新建cloud-consumer-feign-order80

pom

  1. <!--openfeign-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. </dependency>

yml

  1. server:
  2. port: 80
  3. eureka:
  4. client:
  5. register-with-eureka: false
  6. fetch-registry: true
  7. service-url:
  8. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

主启动

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @EnableFeignClients
  4. public class OrderFeignMain80 {
  5. public static void main(String[] args) {
  6. SpringApplication.run(OrderFeignMain80.class, args);
  7. }
  8. }

业务类

业务逻辑接口+@FeignClient配置调用provider服务

新建PaymentFeignService接口并新增注解@FeignClient

  1. @Component
  2. @FeignClient("CLOUD-PAYMENT-SERVICE")
  3. public interface PaymentFeignService {
  4. @GetMapping("/payment/get/{id}")
  5. CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
  6. @GetMapping("/payment/feign/timeout")
  7. String paymentFeignTimeout();
  8. }

控制层Controller

  1. @RestController
  2. @Slf4j
  3. public class OrderFeignController {
  4. @Resource
  5. private PaymentFeignService paymentFeignService;
  6. @GetMapping("/consumer/payment/get/{id}")
  7. public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
  8. return paymentFeignService.getPaymentById(id);
  9. }
  10. @GetMapping("/consumer/payment/feign/timeout")
  11. public String paymentFeignTimeout() {
  12. return paymentFeignService.paymentFeignTimeout();
  13. }
  14. }

测试

先启动2个eureka集群7001/7002

再启动2个微服务8001/8002

启动OpenFeign

http://localhost/consumer/payment/get/31

Feign自带负载均衡配置项

总结:

Spring Cloud - 图23

openfeign超时控制

超时设置,故意设置超时演示出错情况

  1. @GetMapping("/payment/feign/timeout")
  2. public String paymentFeignTimeout() {
  3. try {
  4. TimeUnit.SECONDS.sleep(3);
  5. }catch (InterruptedException e){
  6. e.printStackTrace();
  7. }
  8. return serverport;
  9. }

服务消费方80添加超时方法PaymentFeignService

  1. @Component
  2. @FeignClient("CLOUD-PAYMENT-SERVICE")
  3. public interface PaymentFeignService {
  4. @GetMapping("/payment/get/{id}")
  5. CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
  6. @GetMapping("/payment/feign/timeout")
  7. String paymentFeignTimeout();
  8. }

服务消费方80添加超时方法OrderFeignController

  1. @RestController
  2. @Slf4j
  3. public class OrderFeignController {
  4. @Resource
  5. private PaymentFeignService paymentFeignService;
  6. @GetMapping("/consumer/payment/get/{id}")
  7. public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
  8. return paymentFeignService.getPaymentById(id);
  9. }
  10. @GetMapping("/consumer/payment/feign/timeout")
  11. public String paymentFeignTimeout() {
  12. return paymentFeignService.paymentFeignTimeout();
  13. }
  14. }

测试

http://localhost/consumer/payment/feign/timeout

Spring Cloud - 图24

OpenFeign默认等待1秒钟,超过后报错

是什么

OpenFeign默认支持Ribbon

Spring Cloud - 图25

YML文件里需要开启OpenFeign客户端超时控制

  1. server:
  2. port: 80
  3. eureka:
  4. client:
  5. register-with-eureka: false
  6. fetch-registry: true
  7. service-url:
  8. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  9. # 设置feign客户端超时时间(OpenFeign默认支持ribbon)
  10. ribbon:
  11. # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  12. ReadTimeout: 5000
  13. # 指的是建立连接后从服务器读取到可用资源所用的时间
  14. ConnectTimeout: 5000

openfeign日志打印功能

日志打印功能

是什么

Spring Cloud - 图26

日志级别

Spring Cloud - 图27

配置文件bean

  1. @Configuration
  2. public class FeignConfig {
  3. /**
  4. * feignClient配置日志级别
  5. *
  6. * @return
  7. */
  8. @Bean
  9. public Logger.Level feignLoggerLevel() {
  10. // 请求和响应的头信息,请求和响应的正文及元数据
  11. return Logger.Level.FULL;
  12. }
  13. }

yml文件里需要开启日志的Feign客户端

  1. server:
  2. port: 80
  3. eureka:
  4. client:
  5. register-with-eureka: false
  6. fetch-registry: true
  7. service-url:
  8. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  9. # 设置feign客户端超时时间(OpenFeign默认支持ribbon)
  10. ribbon:
  11. # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  12. ReadTimeout: 5000
  13. # 指的是建立连接后从服务器读取到可用资源所用的时间
  14. ConnectTimeout: 5000
  15. logging:
  16. level:
  17. # feign日志以什么级别监控哪个接口
  18. com.atguigu.springcloud.service.PaymentFeignService: debug

后台日志查看

Spring Cloud - 图28

Hystrix断路器

概述

分布式系统面临的问题复杂分布式体系结构中的应用程序 有数10个依赖关系,每个依赖关系在某些时候将不可避免地失败

Spring Cloud - 图29

Spring Cloud - 图30

是什么

Spring Cloud - 图31

能干嘛

服务降级

服务熔断

接近实时监控

Hystrix重要概念

服务降级

服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback(兜底方案)

当下游的服务因为某种原因突然变得不可用响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

发生场景:

程序运行异常

超时

服务熔断触发服务降级

线程池/信号量也会导致服务降级

服务熔断

类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示

就是保险丝->服务降级->进而熔断->恢复调用链路

服务限流

秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行

Hystrix案例

构建

建Module:新建cloud-provider-hystrix-payment8001

pom:

  1. <!--hystrix-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  5. </dependency>

yml:不变

主启动:

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. public class PaymentHystrixMain8001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(PaymentHystrixMain8001.class, args);
  6. }
  7. }

业务类:

service

  1. @Service
  2. public class PaymentService {
  3. /**
  4. * 正常访问
  5. *
  6. * @param id
  7. * @return
  8. */
  9. public String paymentInfo_OK(Integer id) {
  10. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "O(∩_∩)O哈哈~";
  11. }
  12. /**
  13. * 超时访问
  14. *
  15. * @param id
  16. * @return
  17. */
  18. public String paymentInfo_TimeOut(Integer id) {
  19. int timeNumber = 3;
  20. try {
  21. // 暂停3秒钟
  22. TimeUnit.SECONDS.sleep(timeNumber);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
  27. "O(∩_∩)O哈哈~ 耗时(秒)" + timeNumber;
  28. }
  29. }

controller

  1. @RestController
  2. @Slf4j
  3. public class PaymentController {
  4. @Resource
  5. private PaymentService paymentService;
  6. @Value("${server.port}")
  7. private String servicePort;
  8. /**
  9. * 正常访问
  10. *
  11. * @param id
  12. * @return
  13. */
  14. @GetMapping("/payment/hystrix/ok/{id}")
  15. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  16. String result = paymentService.paymentInfo_OK(id);
  17. log.info("*****result:" + result);
  18. return result;
  19. }
  20. /**
  21. * 超时访问
  22. *
  23. * @param id
  24. * @return
  25. */
  26. @GetMapping("/payment/hystrix/timeout/{id}")
  27. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  28. String result = paymentService.paymentInfo_TimeOut(id);
  29. log.info("*****result:" + result);
  30. return result;
  31. }
  32. }

测试:

  1. 启动eureka7001
  2. 启动eureka-provider-hystrix-payment8001
  3. 访问:

    1. success的方法:http://localhost:8001/payment/hystrix/ok/31
    2. 每次调用耗费5s:http://localhost:8001/payment/hystrix/timeout/31
  4. 上述module均ok。以上述为根基平台,从正确->错误->降级熔断->恢复

高并发测试

上述在非高并发情形下,还能勉强满足 but…

Jmeter压测测试

Jmeter压测结论

上面还只是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死

看热闹不嫌事大,新建80加入

新建cloud-consumer-feign-hystrix-order80

pom与yml,主启动类不变

  1. @Component
  2. @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
  3. public interface PaymentHystrixService {
  4. /**
  5. * 正常访问
  6. *
  7. * @param id
  8. * @return
  9. */
  10. @GetMapping("/payment/hystrix/ok/{id}")
  11. String paymentInfo_OK(@PathVariable("id") Integer id);
  12. /**
  13. * 超时访问
  14. *
  15. * @param id
  16. * @return
  17. */
  18. @GetMapping("/payment/hystrix/timeout/{id}")
  19. String paymentInfo_TimeOut(@PathVariable("id") Integer id);
  20. }

controller

  1. @RestController
  2. @Slf4j
  3. public class OrderHyrixController {
  4. @Resource
  5. private PaymentHystrixService paymentHystrixService;
  6. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  7. String paymentInfo_OK(@PathVariable("id") Integer id){
  8. return paymentHystrixService.paymentInfo_OK(id);
  9. }
  10. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  11. String paymentInfo_TimeOut(@PathVariable("id") Integer id){
  12. return paymentHystrixService.paymentInfo_TimeOut(id);
  13. }
  14. }

正常测试:http://localhost/consumer/payment/hystrix/ok/32

高并发测试:

  1. 2w个线程压8001
  2. 消费者80微服务再去访问的OK服务8001地址
  3. http://localhost/consumer/payment/hystrix/ok/32
  4. 消费者80,o(╥﹏╥)o

    1. 要么转圈圈
    2. 要么消费端报超时错误Spring Cloud - 图33

故障和导致现象

8001同一层次的其他接口被困死,因为tomcat线程池里面的工作线程已经被挤占完毕

80此时调用8001,客户端访问响应缓慢,转圈圈

上述结论

正因为有上述故障或不佳表现 才有我们的降级/容错/限流等技术诞生

如何解决?解决的要求是?

超时导致服务器变慢(转圈):超时不再等待

出错(宕机或者程序运行出错):出错要有兜底

解决:

  • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001)ok,调用者(80)自己有故障或有自我要求(自己的等待时间小于服务提供者)

服务降级

降级配置:

@HystrixCommand

8001先从自身找问题

设置自身调用超时时间的峰值,峰值内可以正常运行, 超过了需要有兜底的方法处理,做服务降级fallback

8001fallback

业务类启用:一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbckMethod调用类中的指定方法

Spring Cloud - 图34

  1. @HystrixCommand(fallbackMethod = "payment_TimeOutHandler", commandProperties = {
  2. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")}

Spring Cloud - 图35

  1. @RestController
  2. @Slf4j
  3. public class OrderHyrixController {
  4. @Resource
  5. private PaymentHystrixService paymentHystrixService;
  6. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  7. String paymentInfo_OK(@PathVariable("id") Integer id){
  8. return paymentHystrixService.paymentInfo_OK(id);
  9. }
  10. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  11. @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
  12. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
  13. })
  14. // @HystrixCommand/*如果配置了上面那个command,就用上面那个,如果只是一个单独的注解,则使用默认的全局配置*/
  15. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  16. //int age = 10/0;
  17. return paymentHystrixService.paymentInfo_TimeOut(id);
  18. }
  19. public String paymentTimeOutFallbackMethod() {
  20. return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
  21. }
  22. }

主启动类激活:@EnableCircuitBreaker

80fallback

80订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端端降级保护

我们自己配置过的热部署方式对java代码的改动明显,但对@HystrixCommand内属性的修改建议重启微服务

pom:

  1. <!--hystrix-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  5. </dependency>
  1. server:
  2. port: 80
  3. eureka:
  4. client:
  5. register-with-eureka: false
  6. fetch-registry: true
  7. service-url:
  8. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  9. feign:
  10. hystrix:
  11. enabled: true

目前问题

每个业务方法对应一个兜底的方法,代码膨胀

统一的方法和自定义的方法分开

解决方法
  1. 每个方法都配置一个,膨胀

    1. feign接口系列

    2. @DefaultProperties(defaultFallback=””) ```java @RestController @Slf4j @DefaultProperties(defaultFallback = “payment_Global_FallbackMethod”) public class OrderHystrixController { @Resource private PaymentHystrixService paymentHystrixService;

  1. @GetMapping("/consumer/payment/hystrix/ok/{id}")
  2. public String paymentInfo_OK(@PathVariable("id") Integer id) {
  3. return paymentHystrixService.paymentInfo_OK(id);
  4. }
  5. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  6. /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
  7. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
  8. })*/
  9. @HystrixCommand
  10. public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
  11. //int age = 10/0;
  12. return paymentHystrixService.paymentInfo_TimeOut(id);
  13. }
  14. public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
  15. return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
  16. }
  17. /**
  18. * 全局fallback
  19. *
  20. * @return
  21. */
  22. public String payment_Global_FallbackMethod() {
  23. return "Global异常处理信息,请稍后重试.o(╥﹏╥)o";
  24. }

}

  1. 2.
  2. 和业务逻辑混在一起,混乱
  3. 1. 服务降级,客户端去调用服务端,碰上服务端宕机或关闭
  4. 2. 本次案例服务降级处理是在客户端80实现完成,与服务端8001没有关系 只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
  5. 3. 未来我们要面对的异常
  6. 1. 运行
  7. 2. 超时
  8. 3. 宕机
  9. 4. 再来看看业务类![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127213031028.png#alt=image-20210127213031028)
  10. 5. 修改cloud-consumer-feign-hystrix-order80
  11. 6. 根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现接口,统一为接口里面的方法进行异常处理
  12. 7. PaymentFallbackService类实现PaymentFeginService接口![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127213150154.png#alt=image-20210127213150154)
  13. 8. yml![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127213224814.png#alt=image-20210127213224814)
  14. 9. PaymentFeignClientService接口![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127213246784.png#alt=image-20210127213246784)
  15. 10. 测试
  16. 1. 单个eureka先启动7001
  17. 2. PaymentHystrixMain8001启动
  18. 3. 正常访问测试:[http://localhost/consumer/payment/hystrix/ok/32](http://localhost/consumer/payment/hystrix/ok/32)
  19. 4. 故意关闭微服务8001
  20. 5. 客户端自己调用提示:此时服务端provider已经downl ,但是我们做了服务降级处理, 让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器
  21. <a name="98cb60e9-1"></a>
  22. #### 服务熔断
  23. <a name="eaea9330"></a>
  24. ##### **断路器**
  25. 一句话就是家里的保险丝
  26. <a name="13934ec3"></a>
  27. ##### **实操**
  28. 修改cloud-provider-hystrix-payment8001
  29. @HystrixProperty里面的那么属性需要查询HystrixCommandProperty这个类
  30. PaymentService![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127220733224.png#alt=image-20210127220733224)
  31. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127220745688.png#alt=image-20210127220745688)
  32. PaymentController
  33. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210127220816037.png#alt=image-20210127220816037)
  34. 测试
  35. 1. 自测cloud-provider-hystrix-payment8001
  36. 2. 正确:[http://localhost:8001/payment/circuit/31](http://localhost:8001/payment/circuit/31)
  37. 3. 错误:[http://localhost:8001/payment/circuit/-31](http://localhost:8001/payment/circuit/-31)
  38. 4. 一次正确,一次错误,trytry
  39. 5. 重点测试:多次正确,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问也不能进行
  40. <a name="cc1d28d4"></a>
  41. ##### 小总结
  42. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214156114.png#alt=image-20210128214156114)
  43. **熔断类型**
  44. - 熔断打开:请求不再调用当前服务,内部设置一般为MTTR(平均故障处理时间),当打开长达导所设时钟则进入半熔断状态
  45. - 熔断关闭:熔断关闭后不会对服务进行熔断
  46. - 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
  47. **官网断路器流程图**
  48. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214327679.png#alt=image-20210128214327679)
  49. 步骤:![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214337994.png#alt=image-20210128214337994)
  50. 断路器在什么情况下开始起作用
  51. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214402277.png#alt=image-20210128214402277)
  52. 断路器开启或者关闭的条件
  53. - 当满足一定的阈值的时候(默认10秒钟超过20个请求次数)
  54. - 当失败率达到一定的时候(默认10秒内超过50%的请求次数)
  55. - 到达以上阈值,断路器将会开启
  56. - 当开启的时候,所有请求都不会进行转发
  57. - 一段时间之后(默认5秒),这个时候断路器是半开状态,会让其他一个请求进行转发. 如果成功,断路器会关闭,若失败,继续开启.重复45
  58. 断路器打开之后的效果
  59. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214515716.png#alt=image-20210128214515716)
  60. ALI配置
  61. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214530034.png#alt=image-20210128214530034)
  62. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128214539526.png#alt=image-20210128214539526)
  63. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128220235439.png#alt=image-20210128220235439)
  64. <a name="9a0f0c74"></a>
  65. ### 服务监控hystrixDashboard
  66. <a name="a4d3b02a-3"></a>
  67. #### 概述
  68. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210128221155473.png#alt=image-20210128221155473)
  69. <a name="dc5f8e55"></a>
  70. #### 仪表盘9001的构建
  71. 新建HystrixDashboardMain9001
  72. pom
  73. ```xml
  74. <!--hystrix dashboard-->
  75. <dependency>
  76. <groupId>org.springframework.cloud</groupId>
  77. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
  78. </dependency>

yml

  1. server:
  2. port: 9001

主启动

  1. @SpringBootApplication
  2. @EnableHystrixDashboard
  3. public class HystrixDashboardMain9001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(HystrixDashboardMain9001.class);
  6. }
  7. }

所有Provider微服务提供类8001/8002/8003都需要以来部署Spring Cloud - 图36

启动HystrixDashboardMain9001该微服务后续将监控微服务8001:http://localhost:9001/hystrix

断路器演示(服务监控hystrixDashboard)

修改cloud-provider-hystrix-payment8001

注意新版本Hystrix需要在主启动MainAppHystrix8001中指定监控路径Spring Cloud - 图37

主启动类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @EnableCircuitBreaker
  4. public class PaymentHystrixMain8001 {
  5. public static void main(String[] args){
  6. SpringApplication.run(PaymentHystrixMain8001.class,args);
  7. }
  8. /**
  9. * 此配置是为了服务监控而配置,与服务容错本身无观,springCloud 升级之后的坑
  10. * ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream
  11. * 只要在自己的项目中配置上下面的servlet即可
  12. * @return
  13. */
  14. @Bean
  15. public ServletRegistrationBean getServlet(){
  16. HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
  17. ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
  18. registrationBean.setLoadOnStartup(1);
  19. registrationBean.addUrlMappings("/hystrix.stream");
  20. registrationBean.setName("HystrixMetricsStreamServlet");
  21. return registrationBean;
  22. }
  23. }

测试监控

启动一个或者多个Eureka集群均可

观察监控窗口:9001监控8001:http://localhost:8001/hystrix.stream

然后执行localhost:8001/payment/circuit/-31和localhost:8001/payment/circuit/31

Spring Cloud - 图38

Gateway网关

概述简介

是什么

SpringCloud Gateway使用的是Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架

Spring Cloud - 图39

Spring Cloud - 图40

Spring Cloud - 图41

Spring Cloud - 图42

源码架构

Spring Cloud - 图43

能干嘛

  • 反向代理
  • 鉴权
  • 流量控制
  • 熔断
  • 日志监控
  • 。。。。。。

微服务架构中,网关的未知

Spring Cloud - 图44

三大核心概念

路由route

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如断言为true则匹配该路由

断言predicate

参考的是Java8的java.util.function.Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

过滤filter

指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改.

总结

Spring Cloud - 图45

gateway工作流程

路由转发+执行过滤器链

Spring Cloud - 图46

Spring Cloud - 图47

入门配置

建module:cloud-gateway-gateway9527

pom: spring-cloud-starter-gateway

主启动类:

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. public class GateWayMain9527 {
  4. public static void main(String[] args){
  5. SpringApplication.run(GateWayMain9527.class,args);
  6. }
  7. }

9527如何做路由映射

cloud-provider-payment8001看看controller的访问地址

  1. @GetMapping(value = "/payment/lb")
  2. public String getPaymentLb() {
  3. return serverport;
  4. }
  5. @GetMapping("/payment/get/{id}")
  6. public CommonResult getPaymentById(@PathVariable("id") Long id) {
  7. Payment payment = paymentService.getPaymentById(id);
  8. log.info("****插入结果****" + payment);
  9. if (payment != null) {
  10. return new CommonResult(200, "success serverport: " + serverport, payment);
  11. } else {
  12. return new CommonResult(444, "failure", null);
  13. }
  14. }

不想暴露8001端口,希望在8001外面套一层9527

新增网关配置

  1. server:
  2. port: 9527
  3. spring:
  4. application:
  5. name: cloud-gateway
  6. cloud:
  7. gateway:
  8. discovery:
  9. locator:
  10. enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
  11. routes:
  12. - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
  13. #匹配后提供服务的路由地址
  14. uri: http://localhost:8001
  15. predicates:
  16. - Path=/payment/get/** # 断言,路径相匹配的进行路由
  17. - id: payment_route2
  18. uri: http://localhost:8001
  19. predicates:
  20. Path=/payment/lb/** #断言,路径相匹配的进行路由
  21. eureka:
  22. instance:
  23. hostname: cloud-gateway-service
  24. client:
  25. fetch-registry: true
  26. register-with-eureka: true
  27. service-url:
  28. defaultZone: http://eureka7001.com:7001/eureka/

测试

启动7001 8001 9527

Spring Cloud - 图48

添加网关前:http://localhost:8001/payment/get/31

添加网关后:http://localhost:9527/payment/get/31

yml配置说明

gateway配置路由网关有两中方式:

yml和写进代码(注入RouteLocator的Bean)

假如想要访问https://news.baidu.com/guonei

通过9527网关访问到外网的百度新闻网址

业务类实现

  1. @Configuration
  2. public class GateWayConfig {
  3. @Bean
  4. public RouteLocator customRouteLocation(RouteLocatorBuilder builder) {
  5. RouteLocatorBuilder.Builder routes = builder.routes();
  6. routes.route("path route atguigu",
  7. r->r.path("/guonei")
  8. .uri("http://news.baidu.com/guonei")).build();
  9. return routes.build();
  10. }
  11. @Bean
  12. public RouteLocator customRouteLocation2(RouteLocatorBuilder builder) {
  13. RouteLocatorBuilder.Builder routes = builder.routes();
  14. routes.route("path route atguigu",
  15. r->r.path("/guoji")
  16. .uri("http://news.baidu.com/guoji")).build();
  17. return routes.build();
  18. }
  19. }

通过服务名实现动态

默认情况下Gatway会根据注册中心注册的服务列表, 以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

启动:一个eureka7001+两个服务提供者8001/8002

  1. server:
  2. port: 9527
  3. spring:
  4. application:
  5. name: cloud-gateway
  6. cloud:
  7. gateway:
  8. discovery:
  9. locator:
  10. enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
  11. routes:
  12. - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
  13. #匹配后提供服务的路由地址
  14. # uri: http://localhost:8001
  15. uri: lb://cloud-payment-service
  16. predicates:
  17. - Path=/payment/get/** # 断言,路径相匹配的进行路由
  18. - id: payment_route2
  19. # uri: http://localhost:8001
  20. uri: lb://cloud-payment-service
  21. predicates:
  22. Path=/payment/lb/** #断言,路径相匹配的进行路由
  23. eureka:
  24. instance:
  25. hostname: cloud-gateway-service
  26. client:
  27. fetch-registry: true
  28. register-with-eureka: true
  29. service-url:
  30. defaultZone: http://eureka7001.com:7001/eureka/

需要注意的是uri的协议lb,表示启用Gateway的负载均衡功能,lb://serverName是spring cloud gatway在微服务中自动为我们创建的负载均衡uri

接着启动http://localhost:9527/payment/lb,8001和8002就互相切换了。

Predicate

是什么

启动9527后有

Spring Cloud - 图49

Route Predicate Factories是什么

Spring Cloud - 图50

Spring Cloud - 图51

常用的Route Predicate

Spring Cloud - 图52

举例说明:After Route Predicate

Spring Cloud - 图53

Spring Cloud - 图54

Filter

是什么

Spring Cloud - 图55

Spring Cloud Gateway的filter

生命周期:

  • pre
  • post

种类:

  • gatewayfilter
  • globalgilter

常用的gatewayfilter

AddRequestParameter

Spring Cloud - 图56

自定义过滤器

自定义全局GlobalFilter

两个主要接口:GlobalFilter,OrderId

  1. @Component
  2. @Slf4j
  3. public class MyLogGatewayFilter implements GlobalFilter, Ordered {
  4. @Override
  5. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  6. log.info("come in global filter: {}", new Date());
  7. ServerHttpRequest request = exchange.getRequest();
  8. String uname = request.getQueryParams().getFirst("uname");
  9. if (uname == null) {
  10. log.info("用户名为null,非法用户");
  11. exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
  12. return exchange.getResponse().setComplete();
  13. }
  14. // 放行
  15. return chain.filter(exchange);
  16. }
  17. /**
  18. * 过滤器加载的顺序 越小,优先级别越高
  19. *
  20. * @return
  21. */
  22. @Override
  23. public int getOrder() {
  24. return 0;
  25. }
  26. }

正确:http://localhost:9527/payment/lb?uname=z3

Nacos

nacos简介

能干嘛

替代Eureka做服务注册中心

替代Config做服务配置中心

各注册中心对比

Spring Cloud - 图57

安装并运行nacos

命令运行成功后直接访问http://localhost:8848/nacos

nacos作为服务注册中心演示

基于nacos的服务提供者

建module: cloudalibaba-provider-payment9001

pom:

父pom:

  1. <!--Spring cloud alibaba 2.1.0.RELEASE-->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  5. <version>${spring.cloud.alibaba.version}</version>
  6. <type>pom</type>
  7. <scope>import</scope>
  8. </dependency>

子pom:

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  4. </dependency>

yml

  1. server:
  2. port: 9001
  3. spring:
  4. application:
  5. name: nacos-payment-provider
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: localhost:8848
  10. management:
  11. endpoints:
  12. web:
  13. exposure:
  14. include: "*"

主启动类

  1. @SpringBootApplication
  2. @EnableDiscoveryClient
  3. public class PaymentMain9001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(PaymentMain9001.class, args);
  6. }
  7. }

业务类

  1. @RestController
  2. public class PaymentController {
  3. @Value("${server.port}")
  4. private String serverport;
  5. @GetMapping("/payment/nacos/{id}")
  6. public String getPayment(@PathVariable("id") Integer id) {
  7. return "nacos registry , serverport: "+serverport+" id: "+id;
  8. }
  9. }

测试

http://localhost:9001/payment/nacos/1

Spring Cloud - 图58

再创建一个一摸一样的9002,为了演示负载均衡。

基于nacos的服务消费者

建module:

cloudalibaba-consumer-nacos-order83

pom 为什么nacos支持负载均衡

Spring Cloud - 图59

yml

Spring Cloud - 图60

业务类

  1. @Configuration
  2. public class ApplicationContextConfig {
  3. @Bean
  4. @LoadBalanced
  5. public RestTemplate restTemplate() {
  6. return new RestTemplate();
  7. }
  8. }
  1. @Slf4j
  2. @RestController
  3. public class OrderNacosController {
  4. @Resource
  5. private RestTemplate restTemplate;
  6. @Value("${service-url.nacos-user-service}")
  7. private String serverURL;
  8. @GetMapping("/consumer/payment/nacos/{id}")
  9. public String paymentInfo(@PathVariable("id") Integer id) {
  10. return restTemplate.getForObject(serverURL+"/payment/nacos/"+id, String.class);
  11. }
  12. }

http://localhost:83/consumer/payment/nacos/13

83访问9001/9002支持负载均衡。

服务中心的对比

Spring Cloud - 图61

Spring Cloud - 图62

nacos作为服务配置中心演示

Nacos作为配置中心-基础配置

建module cloudalibaba-config-nacos-client3377

pom

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
  4. </dependency>

yml配置

Spring Cloud - 图63

bootstrap.yml

  1. server:
  2. port: 3377
  3. spring:
  4. application:
  5. name: nacos-config-client
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: localhost:8848
  10. config:
  11. server-addr: localhost:8848
  12. file-extension: yaml
  13. group: DEV_GROUP
  14. namespace: 82d8defc-af37-4518-b7e0-9fc6ba3857af
  15. #${spring.application.name}-${spring.profile.active}.${spring.cloud.name.config.file-extension}
  16. #nacos-config-client-dev.yml

application.yml

  1. spring:
  2. profiles:
  3. # active: info
  4. # active: test
  5. active: dev #表示开发环境
  6. #通过不同的active引用不同的dataId
  7. #通过修改active,然后再进bootstrap.yml里修改group,获取不同group的值

业务类

  1. @RestController
  2. @RefreshScope //支持动态刷新,配置自动更新
  3. public class ConfigClientController {
  4. @Value("${config.info}")
  5. private String configInfo;
  6. @GetMapping("/config/info")
  7. public String getConfigInfo() {
  8. return configInfo;
  9. }
  10. }

配置信息

Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则

Spring Cloud - 图64

最后公式
Spring Cloud - 图65{spring.profile.active}.${file-extension}
nacos-config-client-dev.yaml

  • prefix 默认为 spring.application.name 的值
  • spring.profile.active 即为当前环境对应的 profile
  • file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置

Spring Cloud - 图66

nacos作为配置中心-分类配置

问题:

Spring Cloud - 图67

Namespace+Group+Data ID三者关系?为什么这么设计?

Spring Cloud - 图68

Spring Cloud - 图69

Spring Cloud - 图70

三种方式加载配置

dataID

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置

默认空间+默认分组+新建dev和test两个DataId

新建dev配置DataId

新建test配置DataId

通过spring.profile.active属性就能进行多环境下配置文件的读取

Spring Cloud - 图71

Group

通过group环境区分

Spring Cloud - 图72

在nacos图形化界面控制台上面新建配置文件DataID

Spring Cloud - 图73

Spring Cloud - 图74

在config下增加一条Group的配置即可. 可配置为DEV_GROUP或TEST_GROUP

namespace

新建dev/test/的Namespace

Spring Cloud - 图75

yml

bootstrap.yml

Spring Cloud - 图76

application.yml

Spring Cloud - 图77

nacos集群和持久化配置

https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

Spring Cloud - 图78

nacos持久化配置解释

nacos默认值带的是嵌入式数据derby:https://github.com/alibaba/nacos/blob/develop/config/pom.xml

derby到mysql切换步骤

  1. nacos-server-1.1.4/nacos/conf目录下找到sql脚本

  2. nacos-mysql.sql执行

  3. nacos-server-1.1.4/nacos/conf目录下找到application.properties

  4. 最下面添加

  5. ```properties spring.datasource.platform=mysql

db.num=1 db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=root db.password=123456

  1. 6.
  2. 启动Nacos,可以看到是个全新的空记录界面,以前是记录在derby
  3. <a name="092590ff"></a>
  4. #### Linux版Nacos+MySQL生产环境配置
  5. <a name="af29a0fa"></a>
  6. ##### 预计需要,1个Nginx+3个nacos注册中心+1个mysql
  7. <a name="96ffd22f"></a>
  8. ##### linux安装nacos
  9. 1. 1.Linux服务器上mysql数据库配置,按照上面的derbymysql切换步骤来执行。
  10. 2. linux上的nacos集群配置cluster.conf:这个IP不能写127.0.0.1,必须是 Linux命令hostname -i能够识别的IP![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131160612805.png#alt=image-20210131160612805)
  11. <a name="9f0519f3"></a>
  12. ##### 编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口
  13. /mynacos/nacos/bin目录下有startup.sh
  14. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131160818787.png#alt=image-20210131160818787)
  15. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131160919264.png#alt=image-20210131160919264)
  16. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131160929283.png#alt=image-20210131160929283)
  17. <a name="35cbaad5"></a>
  18. ##### 配置nginx作为负载均衡器
  19. nginx.conf
  20. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161021826.png#alt=image-20210131161021826)
  21. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161026078.png#alt=image-20210131161026078)
  22. 按照指定配置文件启动
  23. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161042613.png#alt=image-20210131161042613)
  24. <a name="8e54ddfe"></a>
  25. ##### 启动
  26. 截止到此处,1Nginx+3nacos注册中心+1mysql
  27. 访问1111端口的nacos/#login
  28. 新建一个配置测试
  29. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161139067.png#alt=image-20210131161139067)
  30. 会发现linuxmysql插入了一条记录。
  31. <a name="Sentinel"></a>
  32. ## Sentinel
  33. <a name="e05dce83"></a>
  34. ### 简介
  35. [https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#spring_cloud_alibaba_sentinel](https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#spring_cloud_alibaba_sentinel)
  36. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161338036.png#alt=image-20210131161338036)
  37. ![](https://gitee.com/gzh1026/tuc/raw/master/img/image-20210131161318935.png#alt=image-20210131161318935)
  38. <a name="e655a410"></a>
  39. ### 安装
  40. 下载sentinel,执行jar包。打开localhost:8080,密码账号都是sentinel
  41. <a name="3510b014"></a>
  42. ### 初始化功能演示
  43. 启动nacos8848,新建Modulecloudalibaba-sentinel-server8401
  44. pom
  45. ```xml
  46. <dependency>
  47. <groupId>com.alibaba.csp</groupId>
  48. <artifactId>sentinel-datasource-nacos</artifactId>
  49. </dependency>
  50. <dependency>
  51. <groupId>com.alibaba.cloud</groupId>
  52. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  53. </dependency>
  54. <dependency>
  55. <groupId>com.atguigu.springcloud</groupId>
  56. <artifactId>cloud-api-commons</artifactId>
  57. <version>1.0-SNAPSHOT</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>com.alibaba.cloud</groupId>
  61. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  62. </dependency>

yml

  1. server:
  2. port: 8401
  3. spring:
  4. application:
  5. name: sentinel-server
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: localhost:8848
  10. sentinel:
  11. transport:
  12. dashboard: localhost:8080
  13. #备用端口,如果被占用则+1
  14. port: 8719
  15. management:
  16. endpoints:
  17. web:
  18. exposure:
  19. include: "*"

业务类

  1. @RestController
  2. @Slf4j
  3. public class FlowLimitController {
  4. @GetMapping("/testA")
  5. public String testA() {
  6. return "------testA";
  7. }
  8. @GetMapping("/testB")
  9. public String testB() {
  10. log.info("*****" + Thread.currentThread().getName() + "\t" + " /testB");
  11. return "------testB";
  12. }}

启动sentinel8080jar包,启动微服务8401然后查看8080控制台,什么也没出现,是因为sentinel采用懒加载,需要访问一次就出现了

流控规则

基本介绍

Spring Cloud - 图79

Spring Cloud - 图80

流控模式

直接(默认)

直接->快速失败(系统默认)

配置及说明

Spring Cloud - 图81

阈值类型:

  • QPS:抵敌于国外
  • 线程数:关门打狗

测试

快速点击访问http://localhost:8401/testA

结果:Blocked by Sentinel(flow limiting)

此时没有兜底方案,需要有个类似于fallback的兜底方案。

关联

含义

当关联的资源达到阈值时,就限流自己,当与A关联的资源B达到阈值后,就限流自己(B惹事,A挂了,防止雪崩):支付接口达到阈值后,就限流下订单的接口,防止连坐效应。

配置A

Spring Cloud - 图82

此时用postamn模拟并发密集访问testB

Spring Cloud - 图83

然后访问/testA,会发现A挂了。

链路

多个请求调用了同一个微服务。

流控效果

直接->快速失败(默认的流控处理)

直接抛出异常,报错:Blocked by Sentinel(flow limiting)

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

预热(warmup)

说明:公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

Spring Cloud - 图84

Spring Cloud - 图85

默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值

Spring Cloud - 图86

多次点击http://localhost:8401/testB,应用场景:秒杀。

排队等待

匀速排队,阈值必须是QPS

Spring Cloud - 图87

降级规则

官网

https://github.com/alibaba/Sentinel/wiki/熔断降级

基本介绍

Spring Cloud - 图88

Spring Cloud - 图89

Spring Cloud - 图90

Spring Cloud - 图91

Sentinel的断路器是没有半开状态的,半开的状态系统自动去检测是否请求异常, 没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用.具体可以参考Hystrix。

降级策略实战

RT

是什么—平均响应时间

Spring Cloud - 图92

Spring Cloud - 图93

测试
  1. @GetMapping("/testD")
  2. public String testD() {
  3. try {
  4. TimeUnit.SECONDS.sleep(1);
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. log.info("testD 测试RT");
  9. return "------testD";
  10. }

配置

Spring Cloud - 图94

jmeter测压

Spring Cloud - 图95

结论

Spring Cloud - 图96

异常比例

是什么

Spring Cloud - 图97

Spring Cloud - 图98

测试
  1. @GetMapping("/testD")
  2. public String testD() {
  3. int age=10/0;
  4. log.info("testD 测试异常比例");
  5. return "------testD";
  6. }

配置

Spring Cloud - 图99

jmeter测压

Spring Cloud - 图100

结论

Spring Cloud - 图101

异常数

异常数是按分钟统计的

Spring Cloud - 图102

Spring Cloud - 图103

测试

Spring Cloud - 图104

Spring Cloud - 图105

热点key限流

https://github.com/alibaba/Sentinel/wiki/热点参数限流

Spring Cloud - 图106

Spring Cloud - 图107

配置

Spring Cloud - 图108

@SentinelResource(value = "testHotKey")改为

@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")

方法testHotKey里面第一个参数(索引0)只要QPS超过每秒1次,马上降级处理,且此时用的是我们自定义的降级方法。

Spring Cloud - 图109

参数例外项

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

特殊情况:普通是超过1s后达到阈值马上被限流,我们希望p1参数当它是某个值的时候,它的限流和平时不同。特例:假如等于5时,阈值为200.

配置

Spring Cloud - 图110

测试

Spring Cloud - 图111

@SentinelResource

按照资源名称+后续处理

启动nacos、sentinel

建module:cloudalibaba-sentinel-service8401。

业务类
  1. @RestController
  2. public class RateLimitController {
  3. @GetMapping("/byresource")
  4. @SentinelResource(value = "byresource", blockHandler = "handleException")
  5. public CommonResult byResource() {
  6. return new CommonResult(200, "按资源名称限流测试", new Payment(2020L, "serial001"));
  7. }
  8. public CommonResult handleException(BlockException e) {
  9. return new CommonResult(444, e.getClass().getCanonicalName() + "\t服务不可用");
  10. }}

配置流控规则

Spring Cloud - 图112

表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流。

测试

1s一下,ok。

超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生。

Spring Cloud - 图113

通过url地址限流+后续处理

通过访问的URL限流,会返回Sentinel自带默认的限流处理信息

业务类
  1. @RestController
  2. public class RateLimitController {
  3. @GetMapping("/byresource")
  4. @SentinelResource(value = "byresource", blockHandler = "handleException")
  5. public CommonResult byResource() {
  6. return new CommonResult(200, "按资源名称限流测试", new Payment(2020L, "serial001"));
  7. }
  8. public CommonResult handleException(BlockException e) {
  9. return new CommonResult(444, e.getClass().getCanonicalName() + "\t服务不可用");
  10. }
  11. @GetMapping("/ratelimit/byurl")
  12. @SentinelResource(value = "byurl")
  13. public CommonResult byurl() {
  14. return new CommonResult(200, "按url限流成功", new Payment(2020L, "serial02"));
  15. }
  16. }

访问:http://localhost:8401/rateLimit/byUrl

sentinel控制台配置

Spring Cloud - 图114

测试

疯狂点击http://localhost:8401/rateLimit/byUrl

Spring Cloud - 图115

上述兜底方案面临的问题

Spring Cloud - 图116

用户自定义限流处理逻辑

创建customblockhandler类用于自定义限流处理逻辑

自定义限流处理类customblockhandler

  1. public class CustomerBlockHandler {
  2. public static CommonResult handlerException(BlockException e) {
  3. return new CommonResult(4444, "按客户自定义 Method 1 ---- global ExceptionHandler");
  4. }
  5. public static CommonResult handlerException2(BlockException e) {
  6. return new CommonResult(4444, "按客户自定义 Method 2 ---- global ExceptionHandler");
  7. }
  8. }

RateLimitController

  1. @RestController
  2. public class RateLimitController {
  3. @GetMapping("/byresource")
  4. @SentinelResource(value = "byresource", blockHandler = "handleException")
  5. public CommonResult byResource() {
  6. return new CommonResult(200, "按资源名称限流测试", new Payment(2020L, "serial001"));
  7. }
  8. public CommonResult handleException(BlockException e) {
  9. return new CommonResult(444, e.getClass().getCanonicalName() + "\t服务不可用");
  10. }
  11. @GetMapping("/ratelimit/byurl")
  12. @SentinelResource(value = "byurl")
  13. public CommonResult byurl() {
  14. return new CommonResult(200, "按url限流成功", new Payment(2020L, "serial02"));
  15. }
  16. @GetMapping("/ratelimit/comstomerblockhandler")
  17. @SentinelResource(value = "comstomerblockhandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerException2")
  18. public CommonResult comstomerblockhandler() {
  19. return new CommonResult(200, "按客户自定义限流成功", new Payment(2020L, "serial02"));
  20. }
  21. }

然后我们自定义的出来了。

Spring Cloud - 图117

服务熔断功能

sentinel整合ribbon+openfeign+fallback

ribbon系列

服务提供者

启动nacos和sentinel

建立服务提供者9003/9004

业务类

Spring Cloud - 图118

测试:http://localhost:9003/paymentSQL/1

服务消费者

新建cloudalibaba-consumer-nacos-order84

业务类

ApplicationContextConfig

Spring Cloud - 图119

CircleBreakController

目的:

  • fallback管运行异常
  • blockhandler管配置违规

测试地址:http://localhost:84/consumer/fallback/1

没有任何配置

Spring Cloud - 图120

直接返回error页面,不太好。

只配置fallback

Spring Cloud - 图121

只配置blockhandler

Spring Cloud - 图122

Spring Cloud - 图123

结果

Spring Cloud - 图124

fallback和blockhandler都配置

Spring Cloud - 图125

sentinel配置

Spring Cloud - 图126

结果

Spring Cloud - 图127

Spring Cloud - 图128

忽略属性

Spring Cloud - 图129

Spring Cloud - 图130

feign系列

修改84模块

Spring Cloud - 图131

业务类

带@FeignClient注解的业务接口

Spring Cloud - 图132

fallback=PaymentFallbackService.class

Spring Cloud - 图133

http://localhost:84/consumer/paymentSQL/1

测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死

Spring Cloud - 图134

规则持久化

是什么

一旦我们重启应用,sentinel规则消失,生产环境需要将配置规则进行持久化

怎么做

将限流规则持久进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看得到,只要Nacos里面的配置不删除,针对8401上的流控规则持续有效

步骤

修改cloudalibaba-sentinel-server8401

pom
  1. <!-- sentinel-datasource-nacos 后续持久化用 -->
  2. <dependency>
  3. <groupId>com.alibaba.csp</groupId>
  4. <artifactId>sentinel-datasource-nacos</artifactId>
  5. </dependency>

YML

添加nacos数据源配置

Spring Cloud - 图135

Spring Cloud - 图136

添加nacos业务规则配置

Spring Cloud - 图137

Spring Cloud - 图138

Spring Cloud - 图139

启动8401书信sentinel发现业务规则变了

Spring Cloud - 图140

快速访问测试接口

http://localhost:8401/rateLimit/byUrl

Spring Cloud - 图141

停止8401再看sentinel

Spring Cloud - 图142重新启动8401再看sentinel

需要稍等一会,多次调用:http://localhost:8401/rateLimit/byUrl,重新配置出现了,持久化验证通过。