(1) 相关博文地址:

  1. 学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105682.html
  2. 学习一下 SpringCloud (二)-- 服务注册中心 EurekaZookeeperConsulNacos https://www.cnblogs.com/l-y-h/p/14193443.html
  3. 学习一下 SpringCloud (三)-- 服务调用、负载均衡 RibbonOpenFeign https://www.cnblogs.com/l-y-h/p/14238203.html

 

(2)代码地址:

  1. https://github.com/lyh-man/SpringCloudDemo

 

  1. 【问题:】
  2. 通过前面几篇博客介绍,完成了基本项目创建、服务注册中心、服务调用 以及 负载均衡(也即 各个模块 已经能正常通信、共同对外提供服务了)。
  3. 对于一个复杂的分布式系统来说,可能存在数十个模块,且模块之间可能会相互调用(嵌套),
  4. 这就带来了一个问题:
  5. 如果某个核心模块突然宕机(或者不能提供服务了),那么所有调用该 核心模块服务 的模块 将会出现问题,
  6. 类似于 病毒感染,一个模块出现问题,将逐步感染其他模块出现问题,最终导致系统崩溃(也即服务雪崩)。
  7. 【服务雪崩:】
  8. 服务雪崩 指的是 服务提供者 不可用(不能提供服务) 而导致 服务消费者不可用,并逐级放大的过程。
  9. 比如:
  10. 多个微服务之间形成链式调用,AB 调用 CC 调用 DD 调用其他服务等。。。
  11. 如果 D 因某种原因(宕机、网络延迟等) 不能对外提供服务了,将导致 C 访问出现问题,而 C 出现问题,将可能导致 AB 出现问题,也即 问题逐级放大(最终可能引起系统崩溃)。
  12. 【解决:】
  13. 服务降级、服务熔断 是解决 服务雪崩的 常用手段。
  14. 相关技术:
  15. Hystrix(维护状态,不推荐使用)
  16. Sentienl(推荐使用)

 

 

 

 

(1) 服务降级

  1. 【服务降级:】
  2. 服务降级 指的是 当服务器压力 剧增 时,根据当前 业务、流量 情况 对一些服务(一般为非核心业务)进行有策略的降级,确保核心业务正常执行。
  3. 释放非核心服务 占用的服务器资源 确保 核心任务正常执行。
  4. 注:
  5. 可以理解为 损失一部分业务能力,保证系统整体正常运行,从而防止 服务雪崩。
  6. 资源是有限的,请求并发高时,若不对服务进行降级处理,系统可能花费大量资源进行非核心业务处理,导致 核心业务 效率降低,进而影响整体服务性能。
  7. 此处的降级可以理解为 不提供服务 或者 延时提供服务(服务执行暂时不正常,给一个默认的返回结果,等一段时间后,正常提供服务)。
  8. 【服务降级分类:】
  9. 手动降级:
  10. 可以通过修改配置中心配置,并根据事先定义好的逻辑,执行降级逻辑。
  11. 自动降级:
  12. 超时降级:设置超时时间、超时重试次数,请求超时则服务降级,并使用异步机制检测 进行 服务恢复。
  13. 失败次数降级:当请求失败达到一定次数则服务降级,同样使用异步机制检测 进行服务恢复。
  14. 故障降级:服务宕机了则服务降级。
  15. 限流降级:请求访问量过大则服务降级。

 

(2)服务熔断

  1. 【服务熔断:】
  2. 服务熔断 指的是 目标服务不可用 或者 请求响应超时时,为了保证整体服务可用,
  3. 不再调用目标服务,而是直接返回默认处理(释放系统资源),通过某种算法检测到目标服务可用后,则恢复其调用。
  4. 注:
  5. 在一定时间内,服务调用失败次数达到一定比例,则认为 当前服务不可用。
  6. 服务熔断 可以理解为 特殊的 服务降级(即 服务不可用 --> 服务降级 --> 服务调用恢复)。
  7. martinfowler 相关博客地址:】
  8. https://martinfowler.com/bliki/CircuitBreaker.html

 

 

 

(3)服务降级 和 服务熔断 的区别

  1. 【相同点:】
  2. 目标相同:均从 可靠性、可用性 触发,避免系统崩溃(服务雪崩)。
  3. 效果相同:均属于 某功能暂不可用。
  4. 【不同点:】
  5. 服务降级 一般 是从整体考虑,可以手动关闭 非核心业务,确保 核心业务正常执行。
  6. 服务熔断 一般 是某个服务不可用,自动关闭 服务调用,并在一定时间内 重新尝试 恢复该服务调用。
  7. 注(个人理解(仅供参考)):
  8. 服务降级 可以作为 预防措施(手动降级),即 服务并没有出错,但是为了提升系统效率,我主动放弃 一部分非核心业务,保证系统资源足够用于 执行 核心业务。
  9. 服务熔断 就是 服务出错的 解决方案(自动降级),即 服务出错后 的一系列处理。

 

  1. Hystrix:】
  2. Hystrix 是一个用于处理分布式系统 延迟 容错的 开源库,
  3. 目的是 隔离远程系统、服务和第三方库的访问点,停止级联故障,并在不可避免发生故障的复杂分布式系统中实现恢复能力。
  4. 注:
  5. 分布式系统难免出现 阻塞、超时、异常 等问题,Hystrix 可以保证在一个服务出问题时,不影响整个系统使用(避免服务雪崩),提高系统的可用性。
  6. 虽然 Hystrix 已进入维护模式,不再更新,但还是可以学习一下思想、基本使用。
  7. 【常用特性:】
  8. 服务降级
  9. 服务熔断
  10. 服务监控
  11. 【相关地址:】
  12. https://github.com/Netflix/Hystrix

 

 

 

(1)什么是 JMeter ?

  1. JMeter
  2. Apache 的一款基于 Java 的压力测试工具。
  3. 注:
  4. 有兴趣的自行研究,此处不过多赘述。
  5. 【官网下载地址:】
  6. http://jmeter.apache.org/download_jmeter.cgi
  7. JMeter 简单使用:】
  8. https://www.cnblogs.com/stulzq/p/8971531.html

 

 

 

(2)说明

  1. 【说明:】
  2. 此处仅简单演示,不需要启动集群(单机版 Eureka 即可)。
  3. eureka_server_7000 作为 服务注册中心。
  4. eureka_client_producer_8001 作为服务提供者。
  5. eureka_client_consumer_9001 作为服务提供者。
  6. 注:
  7. 单机版 Eureka 可参考:https://www.cnblogs.com/l-y-h/p/14193443.html#_label2_1
  8. 此处使用 RestTemplate 发送请求,使用上一篇 讲到的 OpenFeign 技术亦可。
  9. 【演示说明:】
  10. eureka_client_producer_8001 新定义一个接口 testTimeout(),内部暂停 2 秒模拟业务处理所需时间。
  11. 一般情况下,访问 eureka_client_producer_8001 提供的 getUser() 接口时,会立即响应。
  12. 但是如果大量请求访问 testTimeout(),而将系统资源(线程)耗尽时,
  13. 此时若有请求访问 getUser() 就需要等待 前面请求执行完成后,才能继续处理。
  14. 而此时就可能造成 超时等待 的情况,从而引起一系列问题。
  15. 即:
  16. 并发度低时:
  17. 先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。
  18. 并发度高时:
  19. 若有大量请求访问 /consumer/user/testTimeout,导致系统资源(线程)暂时耗尽,
  20. 此时再访问 /consumer/user/get/{id} 就需要等待一些时间才能返回结果。
  21. 严重时请求会出现超时故障,从而引起系统异常。

 

(3)定义接口
  在 eureka_client_producer_8001 中定义一个新接口 testTimeout()。
  在 eureka_client_consumer_9001 中定义一个新接口 调用 testTimeout()。

  1. eureka_client_producer_8001:】
  2. @GetMapping("/testTimeout")
  3. public Result testTimeout() {
  4. try {
  5. Thread.sleep(2000);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. return Result.ok();
  10. }
  11. eureka_client_consumer_9001:】
  12. @GetMapping("/testTimeout")
  13. public Result testTimeout() {
  14. return restTemplate.getForObject(PRODUCER_URL + "/producer/user/testTimeout", Result.class);
  15. }

 

 

 

 

 

 

(4)启动服务,并使用 JMeter 测试
并发度低时:
  先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。
注:
  看页面的刷新按钮。

 

 

并发度高时:
  使用 JMeter 模拟 200 个线程,循环 100 次,访问 /consumer/user/testTimeout。
  此时再访问 /consumer/user/get/{id} 时,不能瞬间返回结果(等待一段时间)。

 

 

 

 

(5)超时故障
  前面已经演示了高并发情况下可能出现超时等待情况,而若 业务执行时间过长 或者 服务调用设置了超时时间,那么当访问被阻塞时,将有可能引起故障。

  1. 【在声明 RestTemplate 时,定义超时时间】
  2. @Bean
  3. @LoadBalanced // 使用 @LoadBalanced 注解赋予 RestTemplate 负载均衡的能力
  4. public RestTemplate getRestTemplate() {
  5. SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
  6. httpRequestFactory.setConnectTimeout(2000);
  7. httpRequestFactory.setReadTimeout(2000);
  8. return new RestTemplate(httpRequestFactory);
  9. }

 

 

 

 

(1)服务降级使用场景
  服务降级 目的是 防止 服务雪崩,本质也就是在 服务调用 出问题时,应该如何处理。

  1. 【服务降级使用场景:】
  2. 服务器资源耗尽,请求响应慢,导致请求超时。
  3. 服务器宕机 或者 程序执行出错,导致请求出错。
  4. 即:
  5. 服务提供者 响应请求超时了,服务消费者 不能一直等待,需要 服务提供者进行 服务降级,保证 请求在一定的时间内被处理。
  6. 服务提供者 宕机了,服务消费者 不能一直等待,需要 服务消费者进行 服务降级,保证 请求在一定的时间内被处理。
  7. 服务提供者正常,但 服务消费者 出现问题了,需要服务消费者 自行 服务降级。
  8. 注:
  9. 服务降级一般在 服务消费者 中处理,服务提供者 也可以 进行处理。

 

(2)在 服务提供者 上实现服务降级(超时自动降级)
  在 eureka_client_producer_8001 代码基础上进行补充。
Step1:
  引入 hystrix 依赖。

  1. 【引入依赖:】
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  5. </dependency>

 

 

Step2:
  通过 @HystrixCommand 注解 编写 服务降级策略。

  1. 【简单说明:】
  2. @HystrixCommand 表示指定 服务降级 或者 服务熔断的策略。
  3. fallbackMethod 表示服务调用失败(请求超时 或者 程序执行异常)后执行的方法(方法参数要与 原方法一致)。
  4. commandProperties 表示配置参数。
  5. @HystrixProperty 设置具体参数。
  6. 注:
  7. 详细参数情况可以参考 HystrixCommandProperties 类。
  8. com.netflix.hystrix.HystrixCommandProperties
  9. 【定义服务降级策略:】
  10. public Result testTimeoutReserveCase() {
  11. return Result.ok().message("当前服务器繁忙,请稍后再试!!!");
  12. }
  13. // 定义服务降级策略
  14. @HystrixCommand(
  15. // 当请求超时 或者 接口异常时,会调用 fallbackMethod 声明的方法(方法参数要一致)
  16. fallbackMethod = "testTimeoutReserveCase",
  17. commandProperties = {
  18. @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")
  19. }
  20. )
  21. @GetMapping("/testTimeout")
  22. public Result testTimeout() {
  23. try {
  24. Thread.sleep(500);
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }
  28. return Result.ok();
  29. }

 

 

Step3:
  在启动类上添加 @EnableCircuitBreaker 注解,开启服务降级、熔断。

 

 

Step4:
  运行测试(此处演示的是 超时自动降级)。
  此处定义接口超时时间为 1.5 秒,模拟 0.5 秒业务处理时间,使用 JMeter 压测该接口时,与上面演示的类似,会出现请求超时的情况,而一旦请求超时,则会触发 fallbackMethod 方法,直接返回数据,而不会持续等待。
如下图所示。

 

 

(3)配置默认服务降级方法
  通过上面简单演示可以完成 服务降级,但是存在一个问题,如果为每一个接口都绑定一个 fallbackMethod,那么代码将非常冗余。
  通过 @DefaultProperties 注解 定义一个默认的 defaultFallback 方法,接口异常时调用默认的方法,并仅对特殊的接口进行单独处理,从而减少代码冗余。

如下,新增一个 运行时异常,访问接口时,将会调用 globalFallBackMethod() 方法。
而前面特殊定义的 testTimeout 超时后,仍调用 testTimeout_reserve_case() 方法。

  1. @DefaultProperties(defaultFallback = "globalFallBackMethod")
  2. public class UserController {
  3. public Result globalFallBackMethod() {
  4. return Result.ok().message("系统异常,请稍后再试!!!");
  5. }
  6. @GetMapping("/testRuntimeError")
  7. @HystrixCommand
  8. public Result testRuntimeError() {
  9. int temp = 10 / 0;
  10. return Result.ok();
  11. }
  12. }

 

 

 

 

(1)说明

  1. 【说明:】
  2. 上面使用 Hystrix 简单演示了 服务提供者 的服务降级。
  3. 这里使用 OpenFeign 演示 服务消费者 的服务降级。
  4. 注:
  5. 重新新建一个模块 eureka_openfeign_client_consumer_9007 作为服务消费者用于演示。
  6. 可参考上一篇 OpenFeign 的使用:https://www.cnblogs.com/l-y-h/p/14238203.html#_label3_2
  7. 服务提供者仍然是 eureka_client_producer_8001

 

(2)配置 OpenFeign 基本代码环境
Step1:
  创建模块 eureka_openfeign_client_consumer_9007。
  修改父工程 与 当前工程 pom.xml 文件。
  修改配置类。
  在启动类上添加 @EnableFeignClients 注解。

  1. 【依赖:】
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.cloud</groupId>
  8. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  9. </dependency>
  10. application.yml
  11. server:
  12. port: 9007
  13. spring:
  14. application:
  15. name: eureka-openfeign-client-consumer
  16. eureka:
  17. instance:
  18. appname: eureka-openfeign-client-consumer-9007 # 优先级比 spring.application.name 高
  19. instance-id: ${eureka.instance.appname} # 设置当前实例 ID
  20. client:
  21. register-with-eureka: true # 默认为 true,注册到 注册中心
  22. fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
  23. service-url:
  24. # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
  25. defaultZone: http://localhost:7000/eureka
  26. # 设置 OpenFeign 超时时间(OpenFeign 默认支持 Ribbon)
  27. ribbon:
  28. # 指的是建立连接所用的超时时间
  29. ConnectTimeout: 2000
  30. # 指的是建立连接后从服务器获取资源的超时时间(即请求处理的超时时间)
  31. ReadTimeout: 2000

 

 

Step2:
  使用 @FeignClient 编写服务调用。

  1. ProducerFeignService:】
  2. package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;
  3. import com.lyh.springcloud.common.tools.Result;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. @FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001")
  9. @Component
  10. public interface ProducerFeignService {
  11. @GetMapping("/producer/user/get/{id}")
  12. Result getUser(@PathVariable Integer id);
  13. @GetMapping("/producer/user/testTimeout")
  14. Result testFeignTimeout();
  15. @GetMapping("/producer/user/testRuntimeError")
  16. Result testRuntimeError();
  17. }

 

 

Step3:
  编写 controller,并进行测试 openfeign 是否能成功访问服务。

  1. ConsumerController
  2. package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.controller;
  3. import com.lyh.springcloud.common.tools.Result;
  4. import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. @RestController
  11. @RequestMapping("/consumer/user")
  12. public class ConsumerController {
  13. @Autowired
  14. private ProducerFeignService producerFeignService;
  15. @GetMapping("/get/{id}")
  16. public Result getUser(@PathVariable Integer id) {
  17. return producerFeignService.getUser(id);
  18. }
  19. @GetMapping("/testTimeout")
  20. public Result testFeignTimeout() {
  21. return producerFeignService.testFeignTimeout();
  22. }
  23. @GetMapping("/testRuntimeError")
  24. public Result testFeignRuntimeError() {
  25. return producerFeignService.testRuntimeError();
  26. }
  27. }

 

 

 

 

(3)OpenFeign 实现服务降级

  1. 【步骤:】
  2. Step1:在配置文件中,配置 feign.feign.enabled=true,开启服务降级。
  3. Step2:定义一个 实现类,实现 服务调用的 接口,并为每个方法重写 调用失败的逻辑。
  4. Step3:在 @FeignClient 注解中,通过 fallback 参数指定 该实现类。

Step1:
  在配置文件中,开启服务降级。

  1. application.yml
  2. # 开启服务降级
  3. feign:
  4. hystrix:
  5. enabled: true

 

 

Step2:
  定义一个实现类,实现 服务调用的接口。
  @Component 注解不要忘了,启动时可能会报错。

  1. ProducerFeignServiceImpl:】
  2. package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl;
  3. import com.lyh.springcloud.common.tools.Result;
  4. import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
  5. import org.springframework.stereotype.Component;
  6. @Component
  7. public class ProducerFeignServiceImpl implements ProducerFeignService {
  8. @Override
  9. public Result getUser(Integer id) {
  10. return Result.ok().message("系统异常,请稍后再试 -- 11111111111");
  11. }
  12. @Override
  13. public Result testFeignTimeout() {
  14. return Result.ok().message("系统异常,请稍后再试 -- 222222222222");
  15. }
  16. @Override
  17. public Result testRuntimeError() {
  18. return Result.ok().message("系统异常,请稍后再试 -- 333333333333");
  19. }
  20. }

 

 

注:
  未添加 @Component 注解,启动会报下面的错误。

  1. 【报错信息:】
  2. org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'consumerController':
  3. Unsatisfied dependency expressed through field 'producerFeignService'; nested exception is org.springframework.beans.factory.BeanCreationException:
  4. Error creating bean with name 'com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService':
  5. FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException:
  6. No fallback instance of type class com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl.ProducerFeignServiceImpl found for feign client EUREKA-CLIENT-PRODUCER-8001

 

 

Step3:
  在 @FeignClient 注解上,通过 fallback 参数指定上面定义的实现类。

  1. package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;
  2. import com.lyh.springcloud.common.tools.Result;
  3. import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl.ProducerFeignServiceImpl;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. @FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001", fallback = ProducerFeignServiceImpl.class)
  9. @Component
  10. public interface ProducerFeignService {
  11. @GetMapping("/producer/user/get/{id}")
  12. Result getUser(@PathVariable Integer id);
  13. @GetMapping("/producer/user/testTimeout")
  14. Result testFeignTimeout();
  15. @GetMapping("/producer/user/testRuntimeError")
  16. Result testRuntimeError();
  17. }

 

 

Step4:
  简单测试一下。
  当服务提供者 宕机时,此时服务调用失败,将会执行 实现类中的逻辑。
  而服务提供者正常提供服务时,由于上面已经在 服务提供者 处配置了 服务降级,则执行 服务提供者的服务降级策略。

 

 

 

(1)说明

  1. 【服务熔断:】
  2. 服务熔断可以理解为特殊的服务降级,当某个服务出错或者响应时间长时,将会进行服务降级处理,
  3. 从而熔断该服务的调用,但其会检测服务是否正常,若正常,则恢复服务调用。
  4. 注:
  5. 代码方面 上面配置 超时服务自动降级 类似(在其基础上进行代码扩充)。
  6. 【断路器开启、关闭条件:】
  7. 开启条件:
  8. 满足一定的请求阈值(默认 10 秒内请求数超过 20),且失败率达到阈值(默认 10 秒内 50% 的请求失败)。此时将会开启断路器。
  9. 关闭条件:
  10. 断路器开启一段时间后(默认 5 秒),此时断路器处于半开状态,会对其中一部分请求进行转发,如果成功访问,则断路器关闭。
  11. 若请求仍然失败,则再次进入开启状态。

 

 

(2)添加接口配置服务熔断
  在 eureka_client_producer_8001 中新增一个接口,并配置服务熔断逻辑。

  1. 【服务熔断:】
  2. public Result testCircuitBreakerFallBack(@PathVariable Integer id) {
  3. return Result.ok().message("调用失败, ID 不能为负数");
  4. }
  5. @GetMapping("/testCircuitBreaker/{id}")
  6. @HystrixCommand(fallbackMethod = "testCircuitBreakerFallBack", commandProperties = {
  7. // 是否开启断路器。默认为 true。
  8. @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
  9. // 在一定时间内,请求总数达到了阈值,才有资格进行熔断。默认 20 个请求。
  10. @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
  11. // 熔断之后,重新尝试恢复服务调用的时间,在此期间,会执行 fallbackMethod 定义的逻辑。默认 5 秒。
  12. @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
  13. // 出错阈值,请求总数超过了阈值,并且调用失败率达到一定比率,会熔断。默认 50%。
  14. @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")
  15. })
  16. public Result testCircuitBreaker(@PathVariable Integer id) {
  17. if (id < 0) {
  18. throw new RuntimeException("ID 不能为负数");
  19. }
  20. return Result.ok().message("调用成功, ID = " + id);
  21. }

 

 

(3)直接访问该服务测试一下(使用 JMeter 测试亦可)。
  如下图所示,当请求失败达到一定比率,将会开启断路器。
  一段时间后,尝试恢复服务调用,关闭断路器。

 

 

(1)Dashboard
  Hystrix 提供了图形化的监控工具(Hystrix Dashboard)进行准实时的调用监控,其可以持续的记录通过 Hystrix 发送的请求执行信息,并以图形、统计报表的形式呈现给用户。
  SpringCloud 对其进行了整合,导入相关依赖即可。

 

(2)使用
Step1:
  引入依赖(hystrix-dashboard 以及 actuator)。

  1. 【依赖:】
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
  5. </dependency>
  6.  
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-actuator</artifactId>
  10. </dependency>

 

 

Step2:
  在启动类上添加 @EnableHystrixDashboard 注解,开启 Dashboard。

 

 

Step3:
  启动服务后,访问 http://localhost:8001/hystrix,填写需要监控的地址即可。
  开启监控后,访问配置了 @HystrixCommand 注解的服务时,将会触发监控。

 

 

 

 

(1)错误
  使用 Dashboard 最常见的错误就是 Unable to connect to Command Metric Stream。
  根据控制台打印的 日志进行相关配置即可解决此错误。
  错误效果如下图所示,

 

 

(2)错误一与解决:

  1. 【错误一:】
  2. 控制台打印错误如下:
  3. Proxy opening connection to: http://localhost:8001/hystrix.stream
  4. 【解决:】
  5. 在配置类中添加如下配置。
  6. /**
  7. * 错误 Unable to connect to Command Metric Stream. 本质是无法解析 "/hystrix.stream"。
  8. * 自行配置一下即可。
  9. */
  10. @Bean
  11. public ServletRegistrationBean getServlet() {
  12. HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
  13. ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean(streamServlet);
  14. registrationBean.setLoadOnStartup(1);
  15. registrationBean.addUrlMappings("/hystrix.stream");
  16. registrationBean.setName("HystrixMetricsStreamServlet");
  17. return registrationBean;
  18. }

 

 

(3)错误二与解决:

  1. 【错误二:】
  2. 上面解决了连接错误,但是仍然报错。
  3. 控制台打印错误如下:
  4. Origin parameter: http://localhost:8001/hystrix.stream is not in the allowed list of proxy host names.
  5. If it should be allowed add it to hystrix.dashboard.proxyStreamAllowList.
  6. 【解决:】
  7. 在配置文件中配置 proxyStreamAllowList 放行 host 即可。
  8. hystrix:
  9. dashboard:
  10. proxy-stream-allow-list: "*"
  11. # proxy-stream-allow-list: "localhost"

 

 

(1)什么是 Sentinel?

  1. Sentinel:】
  2. Sentinel 是阿里开源的项目,面向云原生微服务的高可用流控防护组件。
  3. 从流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
  4. 注:
  5. 官方文档上写的还是很详细的,并提供了相应的中文文档。
  6. 此处不过多描述,仅介绍使用,相关概念、原理 请自行翻阅文档。
  7. 【官网地址:】
  8. https://github.com/alibaba/Sentinel
  9. https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

Sentinel 主要特性如下(图片来源于网络:)

 

 

(2)Sentinel 组成

  1. Sentinel 组成:】
  2. Sentinel 可以分为两部分:Java 客户端 Dashboard 控制台。
  3. Java 客户端(核心库):
  4. 核心库 不依赖于 任何框架、库,能够运行于 Java7 及以上版本的 Java 运行时环境,
  5. 同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  6. Dashboard 控制台:
  7. 基于 SpringBoot 开发,打包后可直接运行,无需额外的 Tomcat 容器部署。
  8. 控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。
  9. 注:
  10. 控制台使用参考文档:
  11. https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
  12. 控制台 jar 包下载:
  13. https://github.com/alibaba/Sentinel/releases
  14. 注:
  15. 通过 Dashboard 控制台,可以很轻松的通过 web 页面设置 服务降级、熔断等规则。也可以通过代码的方式进行配置。
  16. 个人比较倾向于 web 页面操作,省去了编写代码的工作(视工作环境而定)。
  17. 后面介绍的 Dashboard 操作均以 web 界面进行操作,若想通过代码进行配置,请自行翻阅官方文档。
  18. web 页面编辑的规则 重启服务后 会丢失,需要将其进行持久化处理,一般都是持久化到 nacos 中(后续介绍)。

 

(3)Hystrix 与 Sentinel 区别

  1. 【官网地址:】
  2. https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel
  3. 注:
  4. 详情请自行查阅官网文档。

 

 

 

 

(1)说明

  1. 【说明:】
  2. Sentinel 一般与 Nacos 一起使用,Nacos 使用后续介绍,此处仍使用 Eureka 进行整合。
  3. 新建一个 eureka_client_sentinel_producer_8010 模块(引入核心库依赖)进行演示。
  4. 从官网下载 sentinel-dashboard,用于启动 Dashboard 界面。
  5. 注:
  6. 下载地址:https://github.com/alibaba/Sentinel/releases

 

(2)下载并启动 sentinel-dashboard。
Step1:下载 sentinel-dashboard。

 

 

Step2:命令行启动 jar 包。

  1. 【命令行启动 jar 包:】
  2. java -jar sentinel-dashboard-1.8.0.jar
  3. 注:
  4. 启动后,默认访问端口为 8080,可以通过 -Dserver.port 参数进行修改。
  5. 比如: java -jar -Dserver.port=8888 sentinel-dashboard-1.8.0.jar
  6. 【访问地址:】
  7. 通过 IP + 端口 即可进入登录界面,登录用户、密码 都默认为 sentinel
  8. 比如:http:localhost:8888

 

 

 

 

(3)基本模块 eureka_client_sentinel_producer_8010 创建
Step1:
  修改 父工程、当前工程 pom.xml 文件,并引入相关依赖。

  1. 【直接引入依赖(带上版本号):】
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. <version>2.1.0.RELEASE</version>
  6. </dependency>
  7. 【或者在父工程中统一进行版本管理:】
  8. <properties>
  9. <spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
  10. </properties>
  11.  
  12. <dependencyManagement>
  13. <dependencies>
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  17. <version>${spring.cloud.alibaba.version}</version>
  18. <type>pom</type>
  19. <scope>import</scope>
  20. </dependency>
  21. </dependencies>
  22. </dependencyManagement>
  23. 注:
  24. spring-cloud-alibaba 各版本地址:
  25. https://github.com/alibaba/spring-cloud-alibaba/releases

 

 

Step2:
  修改配置文件,配置 dashboard 信息。

  1. application.yml
  2. server:
  3. port: 8010
  4. spring:
  5. application:
  6. name: eureka_client_sentinel_producer_8010
  7. # 配置 sentinel
  8. cloud:
  9. sentinel:
  10. transport:
  11. # 配置 sentinel-dashboard 地址,此处在本地启动,所以 host 为 localhost
  12. dashboard: localhost:8888
  13. # 应用与 dashboard 交互的端口,默认为 8719 端口
  14. port: 8719
  15. eureka:
  16. instance:
  17. appname: eureka_client_sentinel_producer_8010 # 优先级比 spring.application.name 高
  18. instance-id: ${eureka.instance.appname} # 设置当前实例 ID
  19. client:
  20. register-with-eureka: true # 默认为 true,注册到 注册中心
  21. fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
  22. service-url:
  23. # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
  24. defaultZone: http://localhost:7000/eureka

 

 

Step3:
  编写测试 controller,进行测试。

  1. TestController
  2. package com.spring.cloud.eureka_client_sentinel_producer_8010.controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. @RestController
  7. @RequestMapping("/testSentinel")
  8. public class TestController {
  9. @GetMapping("/hello")
  10. public String hello() {
  11. return "Hello World";
  12. }
  13. @GetMapping("/relation")
  14. public String relation() {
  15. return "relation";
  16. }
  17. }

 

Step4:
  启动 eureka_server_7000、以及当前服务 ,测试一下。
注:
  即使服务启动,Sentinel Dashboard 也是空白的,需要调用一下当前服务的接口,其相关信息才会出现在 Sentinel 中。

 

 

 

 

(1)什么是流量控制(flow control)?

  1. 【流量控制:】
  2. 流量控制 本质上是 监控 应用流量的 QPS 或者 并发线程数等指标,
  3. 当监控的指标达到 阈值 后,将会对访问进行限制,减少请求的正常响应。
  4. 从而避免应用 被瞬间的高并发请求击垮而崩溃,进而保障应用的高可用性。
  5. 注:
  6. QPSQuery Per Second):每秒查询率,即服务每秒能响应的查询(请求)次数。
  7. 【文档地址:】
  8. https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

 

(2)流控规则基本参数

  1. 【相关类:】
  2. com.alibaba.csp.sentinel.slots.block.flow.FlowRule
  3. 注:
  4. 通过代码设置流控规则可以参考如下代码:
  5. https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java
  6. 【流控规则页面参数:】
  7. 资源名:
  8. 默认为 请求的资源路径,唯一。
  9. 针对来源:
  10. 默认为 default,表示不区分来源。
  11. 注:
  12. 网上查阅的资料都说可以根据 微服务名 进行限流,有待确认。
  13. 此处未研究原理,留个坑,后续有时间再去研究。
  14. 阈值类型:
  15. QPS
  16. 当调用该资源接口的 QPS 达到阈值后,将会限流。
  17. 线程数:
  18. 当调用该资源接口的 线程数 达到阈值后,将会限流。
  19. 单机阈值:
  20. 设置 阈值类型(QPS、线程数)的 阈值。
  21. 流控模式:
  22. 直接:
  23. 当资源接口达到限流条件时,会当前资源直接限流。
  24. 关联:
  25. 当某个资源关联的资源达到限流条件时,则 当前资源 被限流。
  26. 链路:
  27. 资源之间的调用形成调用链路,而 链路 模式仅记录 指定入口的流量,如果达到限流条件,则限流。
  28. 流控效果:
  29. 快速失败:
  30. 一旦达到限流条件,则直接抛异常(FlowException),然后走失败的处理逻辑。
  31. Warm Up
  32. 根据冷加载因子(coldFactor,默认 3),刚开始阈值请求数为 原阈值/coldFactor,经过一段时间后,阈值才会变为 原阈值。
  33. 排队等待:
  34. 请求会排队等待执行,匀速通过,此时的 阈值类型必须为 QPS

 

 

(3)演示 — 直接、快速失败。

  1. 【说明:】
  2. 配置 /testSentinel/hello 的流控规则,不区分请求来源,
  3. QPS 超过 1(即 1 秒钟超过 1 次查询)时,将会执行 直接限流,效果为 快速失败(会显示默认错误)。
  4. 【设置流控规则如下:】
  5. 资源名: /testSentinel/hello
  6. 针对来源: default
  7. 阈值类型: QPS
  8. 单机阈值: 1
  9. 流控模式: 直接
  10. 流控效果: 快速失败

 

 

如下图所示,1 秒刷新一次是正常返回的结果,而 1 秒刷新多次后,将会输出默认的错误信息。

 

 

(4)演示 — 关联、快速失败。

  1. 【说明:】
  2. 现有资源 ABA /testSentinel/helloB /testSentinel/relation
  3. 配置 A 的流控规则,不区分请求来源,将 A 关联 B
  4. B QPS 超过 1(即 1 秒钟超过 1 次查询)时,A 将会被限流,效果为 快速失败(会显示默认错误)。
  5. 【设置流控规则如下:】
  6. 资源名: /testSentinel/hello
  7. 针对来源: default
  8. 阈值类型: QPS
  9. 单机阈值: 1
  10. 流控模式: 关联
  11. 关联资源: /testSentinel/relation
  12. 流控效果: 快速失败
  13. 【实际使用场景举例:】
  14. 两个资源之间具有依赖关系或者竞争资源时,可以使用关联。
  15. 比如:
  16. 对数据库 读操作、写操作 进行限制,可以设置写操作优先,当 写操作 过于频繁时,读操作将被限流。

 

 如下图所示,正常访问 A 是没问题的,但是 B 在 1 秒内多次刷新后,A 将会输出默认出错信息。

 

 

(5)演示 — 直接、Wram Up。

  1. 【说明:】
  2. 配置 /testSentinel/hello 的流控规则,不区分请求来源,
  3. 设置 QPS 6,预热时间为 3 秒,则开始 QPS 阈值将为 2,预热时间结束后,QPS 会恢复到 6
  4. 【设置流控规则如下:】
  5. 资源名: /testSentinel/hello
  6. 针对来源: default
  7. 阈值类型: QPS
  8. 单机阈值: 6
  9. 流控模式: 直接
  10. 流控效果: Warm Up
  11. 预热时长: 3
  12. 【实际场景举例:】
  13. 秒杀系统开启瞬间会有很多请求进行访问,如果不做限制,可能一下子系统直接崩溃了。
  14. 采用 Warm Up 方式,给系统一个缓冲时间,慢慢的增大 QPS

 

 如下图所示,开始 QPS 较小,刷新容易报错,3 秒后,QPS 恢复原值,刷新不容易报错。

 

 

(6)演示 —  直接、排队等待

  1. 【说明:】
  2. 配置 /testSentinel/hello 的流控规则,不区分请求来源,
  3. QPS 超过 1 时,请求将会排队等待,超时时间为 2 秒,超时后将会输出错误信息。
  4. 【设置流控规则如下:】
  5. 资源名: /testSentinel/hello
  6. 针对来源: default
  7. 阈值类型: QPS
  8. 单机阈值: 1
  9. 流控模式: 直接
  10. 流控效果: 排队等待
  11. 超时时间: 2000
  12. 【实际场景举例:】
  13. 通常用于处理 间隔性请求。
  14. 比如:
  15. 消息队列,某瞬间的请求很多,但之后却没有请求,此时可以使用排队等待,将请求延迟执行(而不是直接拒绝)。

 

 如下图所示,快速刷新页面时,请求将会排队等待执行,超时后将会报错。

 

 

(1)@SentinelResource 注解
  @SentinelResource 可以等同于 Hystrix 中的 @HystrixCommand 注解进行理解。

  1. 【相关地址:】
  2. https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
  3. @SentinelResource:】
  4. @SentinelResource 注解用于定义资源,并提供了可选的异常处理 以及 fallback 配置项。
  5. value:资源名称,必须项(不能为空)。
  6. blockHandler
  7. 指定限流异常(BlockException)发生后,应该执行的方法。
  8. 注意事项:
  9. 方法访问权限修饰符为 public
  10. 返回值类型 原方法返回值类型一致。
  11. 参数类型 原方法一致,并追加一个 额外参数,参数类型为 BlockException
  12. blockHandler 指定的函数默认需要与 原方法在同一个类中。
  13. blockHandlerClass
  14. 指定限流异常(BlockException)发生后,应该执行的方法(此方法可以位于 其他类)。
  15. 注意事项:
  16. 方法访问权限修饰符为 public
  17. 方法必须是 static 函数,否则无法解析。
  18. fallback
  19. 指定异常(除了 exceptionsToIgnore 指定的异常外的异常)发生后,应该执行的方法。
  20. 注意事项:
  21. 返回值类型 原方法返回值类型一致。
  22. 参数类型 原方法一致,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
  23. fallback 指定的函数默认需要与 原方法在同一个类中。
  24. fallbackClass
  25. 指定异常发生后,应该执行的方法(此方法可以位于 其他类)。
  26. 注意事项:
  27. 方法访问权限修饰符为 public
  28. 方法必须是 static 函数,否则无法解析。
  29. defaultFallback:
  30. 指定异常发生后,执行默认的逻辑(即 通用处理逻辑)。
  31. fallback 类似,但 其指定的方法参数为空,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
  32. fallback defaultFallback 同时存在时,只有 fallback 会生效。
  33. exceptionsToIgnore:
  34. 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中(直接对外抛出原异常)。
  35. @SentinelResource 使用注意事项:】
  36. blockHandler fallback 都配置了,当限流降级异常发生时(即 抛出 BlockException),只会执行 blockHandler 指定的方法。
  37. 若未配置 fallbackblockHandler 时,则限流降级时,则可能直接抛出 BlockException 异常,若方法本身没定义 throws BlockException,则异常将会被 JVM 包装为 UndeclaredThrowableException 异常。
  38. 可以简单的理解为:
  39. blockHandler 用于指定 限流、降级 等异常(BlockException)发生后应该执行的规则。
  40. fallback 用于指定 其他异常(比如: RuntimeException)发生后应该执行的规则。

 

 

(2)@SentinelResource 使用举例

  1. 【说明:】
  2. controller 中新增如下代码,
  3. fallback() 表示异常发生后的回调函数。
  4. defaultFallback() 表示异常发生后默认的回调函数。
  5. blockHandler()、blockHandler2() 表示 流控、降级 BlockException 异常发生后的回调函数。
  6. testBlockHandler() 用于测试 blockHandler 参数。
  7. testFallback() 用于测试 fallback 参数。
  8. testDefaultFallback() 用于测试 defaultFallback 参数。
  9. 【新增代码:】
  10. public String fallback(Integer id, Throwable ex) {
  11. return ex.getMessage();
  12. }
  13. public String defaultFallback(Throwable ex) {
  14. return ex.getMessage();
  15. }
  16. public String blockHandler(BlockException ex) {
  17. return "block Handler";
  18. }
  19. public String blockHandler2(Integer id, BlockException ex) {
  20. return "block Handler --------- 2";
  21. }
  22. @GetMapping("/testBlockHandler")
  23. @SentinelResource(value = "testBlockHandler", blockHandler = "blockHandler")
  24. public String testBlockHandler() {
  25. return "ok";
  26. }
  27. @GetMapping("/testFallback/{id}")
  28. @SentinelResource(value = "testFallback", blockHandler = "blockHandler", fallback = "fallback")
  29. public String testFallback(@PathVariable Integer id) {
  30. if (id > 10) {
  31. throw new RuntimeException("fallback");
  32. }
  33. return "ok";
  34. }
  35. @GetMapping("/testDefaultFallback/{id}")
  36. @SentinelResource(value = "testDefaultFallback", blockHandler = "blockHandler2", defaultFallback = "defaultFallback")
  37. public String testDefaultFallback(@PathVariable Integer id) {
  38. if (id > 10) {
  39. throw new RuntimeException("defaultFallback");
  40. }
  41. return "ok";
  42. }

 

 

Step1:
  访问 testBlockHandler(),测试 blockHandler 执行回调函数。
  如下,给 testBlockHandler 添加流控规则,当 QPS 大于 1 时,将进行限流,此时将会执行 blockHandler 指定的回调函数。
注:
  正常访问 testBlockHandler() 时,sentinel dashboard 会监控到两个资源名,此处应选择 @SentinelResource 注解中 value 定义的资源名,并配置 流控、降级 规则。

 

 

 

 

 

 

Step2:
  访问 testFallback(),测试 fallback 执行回调函数。
  当 id 小于等于 10 时,正常调用。
  当 id 大于 10 时,抛出异常后被 fallback 接收并执行回调函数。
  同样,设置 流控规则,QPS 大于 1 时,限流,但由于 blockHandler 参数指定的回调方法参数 与 原方法不同,所以该回调函数不生效(空白)。
注:
  限流、降级 等异常 执行的是 blockHandler 指定的回调函数。
  而其他异常 执行的是 fallback 指定的回调函数。

 

 

 

 

Step3:
  访问 testDefaultFallback(),测试 defaultFallback 执行回调函数。
  与上例类似,只是此处 blockHandler 回调函数参数 与 原方法相同,可以调用成功。

 

 

 

 

(1)熔断降级
  熔断降级相关概念,前面在 Hystrix 已经介绍了。
  此处仅演示 Sentinel 降级操作。

  1. 【降级策略:】
  2. 慢调用比例(SLOW_REQUEST_RATIO):
  3. 若选择 慢调用比例 作为阈值,需外同时设置几个参数。
  4. 参数:
  5. 慢调用最大响应时间(最大 RT)。当请求响应时间大于该值时,将被统计为慢调用。
  6. 比例阈值。比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。当慢调用比例大于该值时,将会触发熔断机制。
  7. 最小请求数。单位统计时长内接收请求的最小数。
  8. 熔断时长。熔断执行的时间。
  9. 简单解释:
  10. 当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 慢调用比例(超时请求占总请求数的比例) 大于 比例阈值 时,
  11. 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
  12. 熔断时长结束后,会进入探测恢复状态(即 Hystrix 中提到的 HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,若依旧超时,则再次熔断。
  13. 注:
  14. 此处的 HALF-OPEN 状态,来源于官网介绍(针对 Sentinel 1.8.0 及以上版本)。
  15. 旧版本可能没有 HALF-OPEN 状态(没实际验证过)。
  16. 异常比例(ERROR_RATIO):
  17. 异常比例 慢调用比例 类似,异常比例的参数少了个 最大响应时间。
  18. 简单解释:
  19. 当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 异常比例(异常请求占总请求数的比例) 大于 比例阈值 时,
  20. 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
  21. 熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。
  22. 异常数(ERROR_COUNT):
  23. 异常数 异常比例 类似,异常数 将参数 异常比例 变为 异常数(直接监控异常数,而非比例)。
  24. 简单解释:
  25. 当单位统计时长(statIntervalMs)内 异常请求数 超过 异常数阈值 后,
  26. 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
  27. 熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。

 

 

(2)演示 — 异常比例
  在上面 testDefaultFallback() 基础上,添加 降级 规则,演示 异常比例 降级。
注:
  删除添加的流控规则,并指定 降级规则。
  当降级发生时,将会触发 blockHandler 指定的回调方法。

 

 

 

 

(3)系统规则

  1. 【相关文档:】
  2. https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
  3. 【什么是系统规则(系统自适应限流):】
  4. 系统自适应限流 是从 整体维度 应用程序 入口流量 进行控制,即 在调用应用程序的 方法(接口) 前,将请求拦截下来。
  5. 通过自适应的流控策略,让 系统的入口流量 系统的负载 达到一个平衡,即 让系统尽可能 在保证 最大吞吐量的同时 保证 系统整体的稳定性,
  6. 常用指标:
  7. 应用的负载(Load)、
  8. CPU 使用率、
  9. 总体平均响应时间(RT)、
  10. 入口 QPS
  11. 并发线程数 等。

 

 

 

 

(1)说明

  1. 【说明:】
  2. OpenFeign 一般用于消费端,此处以 eureka_client_consumer_9001 为基础,整合 Sentinel
  3. 注:
  4. 此处为了省事,直接用之前创建好的子模块,亦可自行创建新的模块。
  5. 使用流程 Hystrix 类似。

 

(2)整合 Sentinel
Step1:
  在 eureka_client_consumer_9001 基础上引入 依赖。

  1. 【依赖:】
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. </dependency>
  6.  
  7. <!-- alibaba-sentinel -->
  8. <dependency>
  9. <groupId>com.alibaba.cloud</groupId>
  10. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  11. <version>2.2.1.RELEASE</version>
  12. <exclusions>
  13. <exclusion>
  14. <groupId>com.fasterxml.jackson.dataformat</groupId>
  15. <artifactId>jackson-dataformat-xml</artifactId>
  16. </exclusion>
  17. </exclusions>
  18. </dependency>
  19. 【注意事项:(巨坑)】
  20. alibaba-sentinel 的版本可能会影响程序的执行。
  21. 此处使用的版本:
  22. springcloud Hoxton.SR9
  23. spring.cloud.alibaba 2.1.0.RELEASE
  24. springboot 2.3.5.RELEASE
  25. 引入 alibaba-sentinel 依赖后,服务一直无法启动。
  26. 报错:
  27. nested exception is java.lang.AbstractMethodError: Receiver class com.alibaba.cloud.sentinel.feign.SentinelContractHolder does not define or inherit an implementation of the resolved method 'abstract java.util.List parseAndValidateMetadata(java.lang.Class)' of interface feign.Contract.
  28. 具体原因没整明白,但是如上引入依赖后,可以解决问题(有时间再去研究)。

 

 

 

 

Step2:
  修改配置文件。开启 Sentinel 对 Feign 的支持。

  1. application.yml
  2. # 开启 sentinel 对 feign 的支持
  3. feign:
  4. sentinel:
  5. enabled: true

 

 

Step3:
  使用 @FeignClient 编写服务调用。

  1. ProducerFeignService
  2. package com.lyh.springcloud.eureka_client_consumer_9001.service;
  3. import com.lyh.springcloud.eureka_client_consumer_9001.service.impl.ProducerFeignServiceImpl;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. @FeignClient(value = "EUREKA-CLIENT-SENTINEL-PRODUCER-8010", fallback = ProducerFeignServiceImpl.class)
  9. @Component
  10. public interface ProducerFeignService {
  11. @GetMapping("/testSentinel/testDefaultFallback/{id}")
  12. String testDefaultFallback(@PathVariable Integer id);
  13. @GetMapping("/testSentinel/hello")
  14. String hello();
  15. }
  16. ProducerFeignServiceImpl
  17. package com.lyh.springcloud.eureka_client_consumer_9001.service.impl;
  18. import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
  19. import org.springframework.stereotype.Component;
  20. @Component
  21. public class ProducerFeignServiceImpl implements ProducerFeignService {
  22. @Override
  23. public String testDefaultFallback(Integer id) {
  24. return "系统异常,请稍后重试 --------- 1111111111111";
  25. }
  26. @Override
  27. public String hello() {
  28. return "系统异常,请稍后重试 --------- 2222222222222";
  29. }
  30. }

 

 

Step4:
  编写 controller。

  1. TestController
  2. package com.lyh.springcloud.eureka_client_consumer_9001.controller;
  3. import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.PathVariable;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. @RequestMapping("/consumer2")
  10. @RestController
  11. public class TestController {
  12. @Autowired
  13. private ProducerFeignService producerFeignService;
  14. @GetMapping("/testDefaultFallback/{id}")
  15. public String testDefaultFallback(@PathVariable Integer id) {
  16. return producerFeignService.testDefaultFallback(id);
  17. }
  18. @GetMapping("/hello")
  19. public String hello() {
  20. return producerFeignService.hello();
  21. }
  22. }

 

 

Step5:
  在启动类上添加 @EnableFeignClients 注解,开启 feign 功能。

 

 

Step6:
  测试。
  给 testDefaultFallback() 添加流控规则,QPS 大于 1 时将限流。
  QPS 小于等于 1 时,正常访问。
  若远程服务断开后,访问 testDefaultFallback() 将失败,从而执行本地添加的逻辑。

 

 

 

 

 后续使用到 Nacos 再介绍,此处暂时省略。。。

版权声明:本文为l-y-h原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/l-y-h/p/14364167.html