学习一下 SpringCloud (四)-- 服务降级、熔断 Hystrix、Sentinel
学习一下 SpringCloud (四)– 服务降级、熔断 Hystrix、Sentinel
(1) 相关博文地址:
- 学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105682.html
- 学习一下 SpringCloud (二)-- 服务注册中心 Eureka、Zookeeper、Consul、Nacos :https://www.cnblogs.com/l-y-h/p/14193443.html
- 学习一下 SpringCloud (三)-- 服务调用、负载均衡 Ribbon、OpenFeign : https://www.cnblogs.com/l-y-h/p/14238203.html
(2)代码地址:
- https://github.com/lyh-man/SpringCloudDemo
一、引入 服务降级、熔断
1、问题 与 解决
- 【问题:】
- 通过前面几篇博客介绍,完成了基本项目创建、服务注册中心、服务调用 以及 负载均衡(也即 各个模块 已经能正常通信、共同对外提供服务了)。
- 对于一个复杂的分布式系统来说,可能存在数十个模块,且模块之间可能会相互调用(嵌套),
- 这就带来了一个问题:
- 如果某个核心模块突然宕机(或者不能提供服务了),那么所有调用该 核心模块服务 的模块 将会出现问题,
- 类似于 病毒感染,一个模块出现问题,将逐步感染其他模块出现问题,最终导致系统崩溃(也即服务雪崩)。
- 【服务雪崩:】
- 服务雪崩 指的是 服务提供者 不可用(不能提供服务) 而导致 服务消费者不可用,并逐级放大的过程。
- 比如:
- 多个微服务之间形成链式调用,A、B 调用 C,C 调用 D,D 调用其他服务等。。。
- 如果 D 因某种原因(宕机、网络延迟等) 不能对外提供服务了,将导致 C 访问出现问题,而 C 出现问题,将可能导致 A、B 出现问题,也即 问题逐级放大(最终可能引起系统崩溃)。
- 【解决:】
- 服务降级、服务熔断 是解决 服务雪崩的 常用手段。
- 相关技术:
- Hystrix(维护状态,不推荐使用)
- Sentienl(推荐使用)
2、服务降级 与 服务熔断
(1) 服务降级
- 【服务降级:】
- 服务降级 指的是 当服务器压力 剧增 时,根据当前 业务、流量 情况 对一些服务(一般为非核心业务)进行有策略的降级,确保核心业务正常执行。
- 即 释放非核心服务 占用的服务器资源 确保 核心任务正常执行。
- 注:
- 可以理解为 损失一部分业务能力,保证系统整体正常运行,从而防止 服务雪崩。
- 资源是有限的,请求并发高时,若不对服务进行降级处理,系统可能花费大量资源进行非核心业务处理,导致 核心业务 效率降低,进而影响整体服务性能。
- 此处的降级可以理解为 不提供服务 或者 延时提供服务(服务执行暂时不正常,给一个默认的返回结果,等一段时间后,正常提供服务)。
- 【服务降级分类:】
- 手动降级:
- 可以通过修改配置中心配置,并根据事先定义好的逻辑,执行降级逻辑。
- 自动降级:
- 超时降级:设置超时时间、超时重试次数,请求超时则服务降级,并使用异步机制检测 进行 服务恢复。
- 失败次数降级:当请求失败达到一定次数则服务降级,同样使用异步机制检测 进行服务恢复。
- 故障降级:服务宕机了则服务降级。
- 限流降级:请求访问量过大则服务降级。
(2)服务熔断
- 【服务熔断:】
- 服务熔断 指的是 目标服务不可用 或者 请求响应超时时,为了保证整体服务可用,
- 不再调用目标服务,而是直接返回默认处理(释放系统资源),通过某种算法检测到目标服务可用后,则恢复其调用。
- 注:
- 在一定时间内,服务调用失败次数达到一定比例,则认为 当前服务不可用。
- 服务熔断 可以理解为 特殊的 服务降级(即 服务不可用 --> 服务降级 --> 服务调用恢复)。
- 【martinfowler 相关博客地址:】
- https://martinfowler.com/bliki/CircuitBreaker.html
(3)服务降级 和 服务熔断 的区别
- 【相同点:】
- 目标相同:均从 可靠性、可用性 触发,避免系统崩溃(服务雪崩)。
- 效果相同:均属于 某功能暂不可用。
- 【不同点:】
- 服务降级 一般 是从整体考虑,可以手动关闭 非核心业务,确保 核心业务正常执行。
- 服务熔断 一般 是某个服务不可用,自动关闭 服务调用,并在一定时间内 重新尝试 恢复该服务调用。
- 注(个人理解(仅供参考)):
- 服务降级 可以作为 预防措施(手动降级),即 服务并没有出错,但是为了提升系统效率,我主动放弃 一部分非核心业务,保证系统资源足够用于 执行 核心业务。
- 服务熔断 就是 服务出错的 解决方案(自动降级),即 服务出错后 的一系列处理。
二、服务降级、熔断 — Hystrix
1、什么是 Hystrix ?
- 【Hystrix:】
- Hystrix 是一个用于处理分布式系统 延迟 和 容错的 开源库,
- 目的是 隔离远程系统、服务和第三方库的访问点,停止级联故障,并在不可避免发生故障的复杂分布式系统中实现恢复能力。
- 注:
- 分布式系统难免出现 阻塞、超时、异常 等问题,Hystrix 可以保证在一个服务出问题时,不影响整个系统使用(避免服务雪崩),提高系统的可用性。
- 虽然 Hystrix 已进入维护模式,不再更新,但还是可以学习一下思想、基本使用。
- 【常用特性:】
- 服务降级
- 服务熔断
- 服务监控
- 【相关地址:】
- https://github.com/Netflix/Hystrix
2、使用 JMeter 模拟超时故障发生
(1)什么是 JMeter ?
- 【JMeter】
- Apache 的一款基于 Java 的压力测试工具。
- 注:
- 有兴趣的自行研究,此处不过多赘述。
- 【官网下载地址:】
- http://jmeter.apache.org/download_jmeter.cgi
- 【JMeter 简单使用:】
- https://www.cnblogs.com/stulzq/p/8971531.html
(2)说明
- 【说明:】
- 此处仅简单演示,不需要启动集群(单机版 Eureka 即可)。
- eureka_server_7000 作为 服务注册中心。
- eureka_client_producer_8001 作为服务提供者。
- eureka_client_consumer_9001 作为服务提供者。
- 注:
- 单机版 Eureka 可参考:https://www.cnblogs.com/l-y-h/p/14193443.html#_label2_1
- 此处使用 RestTemplate 发送请求,使用上一篇 讲到的 OpenFeign 技术亦可。
- 【演示说明:】
- 在 eureka_client_producer_8001 新定义一个接口 testTimeout(),内部暂停 2 秒模拟业务处理所需时间。
- 一般情况下,访问 eureka_client_producer_8001 提供的 getUser() 接口时,会立即响应。
- 但是如果大量请求访问 testTimeout(),而将系统资源(线程)耗尽时,
- 此时若有请求访问 getUser() 就需要等待 前面请求执行完成后,才能继续处理。
- 而此时就可能造成 超时等待 的情况,从而引起一系列问题。
- 即:
- 并发度低时:
- 先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。
- 并发度高时:
- 若有大量请求访问 /consumer/user/testTimeout,导致系统资源(线程)暂时耗尽,
- 此时再访问 /consumer/user/get/{id} 就需要等待一些时间才能返回结果。
- 严重时请求会出现超时故障,从而引起系统异常。
(3)定义接口
在 eureka_client_producer_8001 中定义一个新接口 testTimeout()。
在 eureka_client_consumer_9001 中定义一个新接口 调用 testTimeout()。
- 【eureka_client_producer_8001:】
- @GetMapping("/testTimeout")
- public Result testTimeout() {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return Result.ok();
- }
- 【eureka_client_consumer_9001:】
- @GetMapping("/testTimeout")
- public Result testTimeout() {
- return restTemplate.getForObject(PRODUCER_URL + "/producer/user/testTimeout", Result.class);
- }
(4)启动服务,并使用 JMeter 测试
并发度低时:
先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。
注:
看页面的刷新按钮。
并发度高时:
使用 JMeter 模拟 200 个线程,循环 100 次,访问 /consumer/user/testTimeout。
此时再访问 /consumer/user/get/{id} 时,不能瞬间返回结果(等待一段时间)。
(5)超时故障
前面已经演示了高并发情况下可能出现超时等待情况,而若 业务执行时间过长 或者 服务调用设置了超时时间,那么当访问被阻塞时,将有可能引起故障。
- 【在声明 RestTemplate 时,定义超时时间】
- @Bean
- @LoadBalanced // 使用 @LoadBalanced 注解赋予 RestTemplate 负载均衡的能力
- public RestTemplate getRestTemplate() {
- SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
- httpRequestFactory.setConnectTimeout(2000);
- httpRequestFactory.setReadTimeout(2000);
- return new RestTemplate(httpRequestFactory);
- }
3、Hystrix 实现服务降级
(1)服务降级使用场景
服务降级 目的是 防止 服务雪崩,本质也就是在 服务调用 出问题时,应该如何处理。
- 【服务降级使用场景:】
- 服务器资源耗尽,请求响应慢,导致请求超时。
- 服务器宕机 或者 程序执行出错,导致请求出错。
- 即:
- 服务提供者 响应请求超时了,服务消费者 不能一直等待,需要 服务提供者进行 服务降级,保证 请求在一定的时间内被处理。
- 服务提供者 宕机了,服务消费者 不能一直等待,需要 服务消费者进行 服务降级,保证 请求在一定的时间内被处理。
- 服务提供者正常,但 服务消费者 出现问题了,需要服务消费者 自行 服务降级。
- 注:
- 服务降级一般在 服务消费者 中处理,服务提供者 也可以 进行处理。
(2)在 服务提供者 上实现服务降级(超时自动降级)
在 eureka_client_producer_8001 代码基础上进行补充。
Step1:
引入 hystrix 依赖。
- 【引入依赖:】
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
- </dependency>
Step2:
通过 @HystrixCommand 注解 编写 服务降级策略。
- 【简单说明:】
- @HystrixCommand 表示指定 服务降级 或者 服务熔断的策略。
- fallbackMethod 表示服务调用失败(请求超时 或者 程序执行异常)后执行的方法(方法参数要与 原方法一致)。
- commandProperties 表示配置参数。
- @HystrixProperty 设置具体参数。
- 注:
- 详细参数情况可以参考 HystrixCommandProperties 类。
- com.netflix.hystrix.HystrixCommandProperties
- 【定义服务降级策略:】
- public Result testTimeoutReserveCase() {
- return Result.ok().message("当前服务器繁忙,请稍后再试!!!");
- }
- // 定义服务降级策略
- @HystrixCommand(
- // 当请求超时 或者 接口异常时,会调用 fallbackMethod 声明的方法(方法参数要一致)
- fallbackMethod = "testTimeoutReserveCase",
- commandProperties = {
- @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")
- }
- )
- @GetMapping("/testTimeout")
- public Result testTimeout() {
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return Result.ok();
- }
Step3:
在启动类上添加 @EnableCircuitBreaker 注解,开启服务降级、熔断。
Step4:
运行测试(此处演示的是 超时自动降级)。
此处定义接口超时时间为 1.5 秒,模拟 0.5 秒业务处理时间,使用 JMeter 压测该接口时,与上面演示的类似,会出现请求超时的情况,而一旦请求超时,则会触发 fallbackMethod 方法,直接返回数据,而不会持续等待。
如下图所示。
(3)配置默认服务降级方法
通过上面简单演示可以完成 服务降级,但是存在一个问题,如果为每一个接口都绑定一个 fallbackMethod,那么代码将非常冗余。
通过 @DefaultProperties 注解 定义一个默认的 defaultFallback 方法,接口异常时调用默认的方法,并仅对特殊的接口进行单独处理,从而减少代码冗余。
如下,新增一个 运行时异常,访问接口时,将会调用 globalFallBackMethod() 方法。
而前面特殊定义的 testTimeout 超时后,仍调用 testTimeout_reserve_case() 方法。
- @DefaultProperties(defaultFallback = "globalFallBackMethod")
- public class UserController {
- public Result globalFallBackMethod() {
- return Result.ok().message("系统异常,请稍后再试!!!");
- }
- @GetMapping("/testRuntimeError")
- @HystrixCommand
- public Result testRuntimeError() {
- int temp = 10 / 0;
- return Result.ok();
- }
- }
4、OpenFeign 实现服务降级
(1)说明
- 【说明:】
- 上面使用 Hystrix 简单演示了 服务提供者 的服务降级。
- 这里使用 OpenFeign 演示 服务消费者 的服务降级。
- 注:
- 重新新建一个模块 eureka_openfeign_client_consumer_9007 作为服务消费者用于演示。
- 可参考上一篇 OpenFeign 的使用:https://www.cnblogs.com/l-y-h/p/14238203.html#_label3_2
- 服务提供者仍然是 eureka_client_producer_8001。
(2)配置 OpenFeign 基本代码环境
Step1:
创建模块 eureka_openfeign_client_consumer_9007。
修改父工程 与 当前工程 pom.xml 文件。
修改配置类。
在启动类上添加 @EnableFeignClients 注解。
- 【依赖:】
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
- </dependency>
- 【application.yml】
- server:
- port: 9007
- spring:
- application:
- name: eureka-openfeign-client-consumer
- eureka:
- instance:
- appname: eureka-openfeign-client-consumer-9007 # 优先级比 spring.application.name 高
- instance-id: ${eureka.instance.appname} # 设置当前实例 ID
- client:
- register-with-eureka: true # 默认为 true,注册到 注册中心
- fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
- service-url:
- # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
- defaultZone: http://localhost:7000/eureka
- # 设置 OpenFeign 超时时间(OpenFeign 默认支持 Ribbon)
- ribbon:
- # 指的是建立连接所用的超时时间
- ConnectTimeout: 2000
- # 指的是建立连接后从服务器获取资源的超时时间(即请求处理的超时时间)
- ReadTimeout: 2000
Step2:
使用 @FeignClient 编写服务调用。
- 【ProducerFeignService:】
- package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;
- import com.lyh.springcloud.common.tools.Result;
- import org.springframework.cloud.openfeign.FeignClient;
- import org.springframework.stereotype.Component;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- @FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001")
- @Component
- public interface ProducerFeignService {
- @GetMapping("/producer/user/get/{id}")
- Result getUser(@PathVariable Integer id);
- @GetMapping("/producer/user/testTimeout")
- Result testFeignTimeout();
- @GetMapping("/producer/user/testRuntimeError")
- Result testRuntimeError();
- }
Step3:
编写 controller,并进行测试 openfeign 是否能成功访问服务。
- 【ConsumerController】
- package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.controller;
- import com.lyh.springcloud.common.tools.Result;
- import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- @RestController
- @RequestMapping("/consumer/user")
- public class ConsumerController {
- @Autowired
- private ProducerFeignService producerFeignService;
- @GetMapping("/get/{id}")
- public Result getUser(@PathVariable Integer id) {
- return producerFeignService.getUser(id);
- }
- @GetMapping("/testTimeout")
- public Result testFeignTimeout() {
- return producerFeignService.testFeignTimeout();
- }
- @GetMapping("/testRuntimeError")
- public Result testFeignRuntimeError() {
- return producerFeignService.testRuntimeError();
- }
- }
(3)OpenFeign 实现服务降级
- 【步骤:】
- Step1:在配置文件中,配置 feign.feign.enabled=true,开启服务降级。
- Step2:定义一个 实现类,实现 服务调用的 接口,并为每个方法重写 调用失败的逻辑。
- Step3:在 @FeignClient 注解中,通过 fallback 参数指定 该实现类。
Step1:
在配置文件中,开启服务降级。
- 【application.yml】
- # 开启服务降级
- feign:
- hystrix:
- enabled: true
Step2:
定义一个实现类,实现 服务调用的接口。
@Component 注解不要忘了,启动时可能会报错。
- 【ProducerFeignServiceImpl:】
- package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl;
- import com.lyh.springcloud.common.tools.Result;
- import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
- import org.springframework.stereotype.Component;
- @Component
- public class ProducerFeignServiceImpl implements ProducerFeignService {
- @Override
- public Result getUser(Integer id) {
- return Result.ok().message("系统异常,请稍后再试 -- 11111111111");
- }
- @Override
- public Result testFeignTimeout() {
- return Result.ok().message("系统异常,请稍后再试 -- 222222222222");
- }
- @Override
- public Result testRuntimeError() {
- return Result.ok().message("系统异常,请稍后再试 -- 333333333333");
- }
- }
注:
未添加 @Component 注解,启动会报下面的错误。
- 【报错信息:】
- org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'consumerController':
- Unsatisfied dependency expressed through field 'producerFeignService'; nested exception is org.springframework.beans.factory.BeanCreationException:
- Error creating bean with name 'com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService':
- FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException:
- 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 参数指定上面定义的实现类。
- package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;
- import com.lyh.springcloud.common.tools.Result;
- import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl.ProducerFeignServiceImpl;
- import org.springframework.cloud.openfeign.FeignClient;
- import org.springframework.stereotype.Component;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- @FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001", fallback = ProducerFeignServiceImpl.class)
- @Component
- public interface ProducerFeignService {
- @GetMapping("/producer/user/get/{id}")
- Result getUser(@PathVariable Integer id);
- @GetMapping("/producer/user/testTimeout")
- Result testFeignTimeout();
- @GetMapping("/producer/user/testRuntimeError")
- Result testRuntimeError();
- }
Step4:
简单测试一下。
当服务提供者 宕机时,此时服务调用失败,将会执行 实现类中的逻辑。
而服务提供者正常提供服务时,由于上面已经在 服务提供者 处配置了 服务降级,则执行 服务提供者的服务降级策略。
5、Hystrix 实现服务熔断
(1)说明
- 【服务熔断:】
- 服务熔断可以理解为特殊的服务降级,当某个服务出错或者响应时间长时,将会进行服务降级处理,
- 从而熔断该服务的调用,但其会检测服务是否正常,若正常,则恢复服务调用。
- 注:
- 代码方面 与 上面配置 超时服务自动降级 类似(在其基础上进行代码扩充)。
- 【断路器开启、关闭条件:】
- 开启条件:
- 满足一定的请求阈值(默认 10 秒内请求数超过 20),且失败率达到阈值(默认 10 秒内 50% 的请求失败)。此时将会开启断路器。
- 关闭条件:
- 断路器开启一段时间后(默认 5 秒),此时断路器处于半开状态,会对其中一部分请求进行转发,如果成功访问,则断路器关闭。
- 若请求仍然失败,则再次进入开启状态。
(2)添加接口配置服务熔断
在 eureka_client_producer_8001 中新增一个接口,并配置服务熔断逻辑。
- 【服务熔断:】
- public Result testCircuitBreakerFallBack(@PathVariable Integer id) {
- return Result.ok().message("调用失败, ID 不能为负数");
- }
- @GetMapping("/testCircuitBreaker/{id}")
- @HystrixCommand(fallbackMethod = "testCircuitBreakerFallBack", commandProperties = {
- // 是否开启断路器。默认为 true。
- @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
- // 在一定时间内,请求总数达到了阈值,才有资格进行熔断。默认 20 个请求。
- @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
- // 熔断之后,重新尝试恢复服务调用的时间,在此期间,会执行 fallbackMethod 定义的逻辑。默认 5 秒。
- @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
- // 出错阈值,请求总数超过了阈值,并且调用失败率达到一定比率,会熔断。默认 50%。
- @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")
- })
- public Result testCircuitBreaker(@PathVariable Integer id) {
- if (id < 0) {
- throw new RuntimeException("ID 不能为负数");
- }
- return Result.ok().message("调用成功, ID = " + id);
- }
(3)直接访问该服务测试一下(使用 JMeter 测试亦可)。
如下图所示,当请求失败达到一定比率,将会开启断路器。
一段时间后,尝试恢复服务调用,关闭断路器。
6、Hystrix Dashboard
(1)Dashboard
Hystrix 提供了图形化的监控工具(Hystrix Dashboard)进行准实时的调用监控,其可以持续的记录通过 Hystrix 发送的请求执行信息,并以图形、统计报表的形式呈现给用户。
SpringCloud 对其进行了整合,导入相关依赖即可。
(2)使用
Step1:
引入依赖(hystrix-dashboard 以及 actuator)。
- 【依赖:】
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
Step2:
在启动类上添加 @EnableHystrixDashboard 注解,开启 Dashboard。
Step3:
启动服务后,访问 http://localhost:8001/hystrix,填写需要监控的地址即可。
开启监控后,访问配置了 @HystrixCommand 注解的服务时,将会触发监控。
7、Dashboard 错误解决(Unable to connect to Command Metric Stream.)
(1)错误
使用 Dashboard 最常见的错误就是 Unable to connect to Command Metric Stream。
根据控制台打印的 日志进行相关配置即可解决此错误。
错误效果如下图所示,
(2)错误一与解决:
- 【错误一:】
- 控制台打印错误如下:
- Proxy opening connection to: http://localhost:8001/hystrix.stream
- 【解决:】
- 在配置类中添加如下配置。
- /**
- * 错误 Unable to connect to Command Metric Stream. 本质是无法解析 "/hystrix.stream"。
- * 自行配置一下即可。
- */
- @Bean
- public ServletRegistrationBean getServlet() {
- HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
- ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean(streamServlet);
- registrationBean.setLoadOnStartup(1);
- registrationBean.addUrlMappings("/hystrix.stream");
- registrationBean.setName("HystrixMetricsStreamServlet");
- return registrationBean;
- }
(3)错误二与解决:
- 【错误二:】
- 上面解决了连接错误,但是仍然报错。
- 控制台打印错误如下:
- Origin parameter: http://localhost:8001/hystrix.stream is not in the allowed list of proxy host names.
- If it should be allowed add it to hystrix.dashboard.proxyStreamAllowList.
- 【解决:】
- 在配置文件中配置 proxyStreamAllowList 放行 host 即可。
- hystrix:
- dashboard:
- proxy-stream-allow-list: "*"
- # proxy-stream-allow-list: "localhost"
三、服务降级、熔断 — Sentinel
1、什么是 Sentinel?
(1)什么是 Sentinel?
- 【Sentinel:】
- Sentinel 是阿里开源的项目,面向云原生微服务的高可用流控防护组件。
- 从流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
- 注:
- 官方文档上写的还是很详细的,并提供了相应的中文文档。
- 此处不过多描述,仅介绍使用,相关概念、原理 请自行翻阅文档。
- 【官网地址:】
- https://github.com/alibaba/Sentinel
- https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
Sentinel 主要特性如下(图片来源于网络:)
(2)Sentinel 组成
- 【Sentinel 组成:】
- Sentinel 可以分为两部分:Java 客户端 、Dashboard 控制台。
- Java 客户端(核心库):
- 核心库 不依赖于 任何框架、库,能够运行于 Java7 及以上版本的 Java 运行时环境,
- 同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- Dashboard 控制台:
- 基于 SpringBoot 开发,打包后可直接运行,无需额外的 Tomcat 容器部署。
- 控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。
- 注:
- 控制台使用参考文档:
- https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
- 控制台 jar 包下载:
- https://github.com/alibaba/Sentinel/releases
- 注:
- 通过 Dashboard 控制台,可以很轻松的通过 web 页面设置 服务降级、熔断等规则。也可以通过代码的方式进行配置。
- 个人比较倾向于 web 页面操作,省去了编写代码的工作(视工作环境而定)。
- 后面介绍的 Dashboard 操作均以 web 界面进行操作,若想通过代码进行配置,请自行翻阅官方文档。
- web 页面编辑的规则 在 重启服务后 会丢失,需要将其进行持久化处理,一般都是持久化到 nacos 中(后续介绍)。
(3)Hystrix 与 Sentinel 区别
- 【官网地址:】
- https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel
- 注:
- 详情请自行查阅官网文档。
2、基本模块构建
(1)说明
- 【说明:】
- Sentinel 一般与 Nacos 一起使用,Nacos 使用后续介绍,此处仍使用 Eureka 进行整合。
- 新建一个 eureka_client_sentinel_producer_8010 模块(引入核心库依赖)进行演示。
- 从官网下载 sentinel-dashboard,用于启动 Dashboard 界面。
- 注:
- 下载地址:https://github.com/alibaba/Sentinel/releases
(2)下载并启动 sentinel-dashboard。
Step1:下载 sentinel-dashboard。
Step2:命令行启动 jar 包。
- 【命令行启动 jar 包:】
- java -jar sentinel-dashboard-1.8.0.jar
- 注:
- 启动后,默认访问端口为 8080,可以通过 -Dserver.port 参数进行修改。
- 比如: java -jar -Dserver.port=8888 sentinel-dashboard-1.8.0.jar
- 【访问地址:】
- 通过 IP + 端口 即可进入登录界面,登录用户、密码 都默认为 sentinel。
- 比如:http:localhost:8888
(3)基本模块 eureka_client_sentinel_producer_8010 创建
Step1:
修改 父工程、当前工程 pom.xml 文件,并引入相关依赖。
- 【直接引入依赖(带上版本号):】
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- <version>2.1.0.RELEASE</version>
- </dependency>
- 【或者在父工程中统一进行版本管理:】
- <properties>
- <spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>${spring.cloud.alibaba.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- 注:
- spring-cloud-alibaba 各版本地址:
- https://github.com/alibaba/spring-cloud-alibaba/releases
Step2:
修改配置文件,配置 dashboard 信息。
- 【application.yml】
- server:
- port: 8010
- spring:
- application:
- name: eureka_client_sentinel_producer_8010
- # 配置 sentinel
- cloud:
- sentinel:
- transport:
- # 配置 sentinel-dashboard 地址,此处在本地启动,所以 host 为 localhost
- dashboard: localhost:8888
- # 应用与 dashboard 交互的端口,默认为 8719 端口
- port: 8719
- eureka:
- instance:
- appname: eureka_client_sentinel_producer_8010 # 优先级比 spring.application.name 高
- instance-id: ${eureka.instance.appname} # 设置当前实例 ID
- client:
- register-with-eureka: true # 默认为 true,注册到 注册中心
- fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
- service-url:
- # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
- defaultZone: http://localhost:7000/eureka
Step3:
编写测试 controller,进行测试。
- 【TestController】
- package com.spring.cloud.eureka_client_sentinel_producer_8010.controller;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- @RestController
- @RequestMapping("/testSentinel")
- public class TestController {
- @GetMapping("/hello")
- public String hello() {
- return "Hello World";
- }
- @GetMapping("/relation")
- public String relation() {
- return "relation";
- }
- }
Step4:
启动 eureka_server_7000、以及当前服务 ,测试一下。
注:
即使服务启动,Sentinel Dashboard 也是空白的,需要调用一下当前服务的接口,其相关信息才会出现在 Sentinel 中。
3、Sentinel Dashboard 使用 — 流控规则
(1)什么是流量控制(flow control)?
- 【流量控制:】
- 流量控制 本质上是 监控 应用流量的 QPS 或者 并发线程数等指标,
- 当监控的指标达到 阈值 后,将会对访问进行限制,减少请求的正常响应。
- 从而避免应用 被瞬间的高并发请求击垮而崩溃,进而保障应用的高可用性。
- 注:
- QPS(Query Per Second):每秒查询率,即服务每秒能响应的查询(请求)次数。
- 【文档地址:】
- https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
(2)流控规则基本参数
- 【相关类:】
- com.alibaba.csp.sentinel.slots.block.flow.FlowRule
- 注:
- 通过代码设置流控规则可以参考如下代码:
- https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java
- 【流控规则页面参数:】
- 资源名:
- 默认为 请求的资源路径,唯一。
- 针对来源:
- 默认为 default,表示不区分来源。
- 注:
- 网上查阅的资料都说可以根据 微服务名 进行限流,有待确认。
- 此处未研究原理,留个坑,后续有时间再去研究。
- 阈值类型:
- QPS:
- 当调用该资源接口的 QPS 达到阈值后,将会限流。
- 线程数:
- 当调用该资源接口的 线程数 达到阈值后,将会限流。
- 单机阈值:
- 设置 阈值类型(QPS、线程数)的 阈值。
- 流控模式:
- 直接:
- 当资源接口达到限流条件时,会当前资源直接限流。
- 关联:
- 当某个资源关联的资源达到限流条件时,则 当前资源 被限流。
- 链路:
- 资源之间的调用形成调用链路,而 链路 模式仅记录 指定入口的流量,如果达到限流条件,则限流。
- 流控效果:
- 快速失败:
- 一旦达到限流条件,则直接抛异常(FlowException),然后走失败的处理逻辑。
- Warm Up:
- 根据冷加载因子(coldFactor,默认 3),刚开始阈值请求数为 原阈值/coldFactor,经过一段时间后,阈值才会变为 原阈值。
- 排队等待:
- 请求会排队等待执行,匀速通过,此时的 阈值类型必须为 QPS。
(3)演示 — 直接、快速失败。
- 【说明:】
- 配置 /testSentinel/hello 的流控规则,不区分请求来源,
- 当 QPS 超过 1(即 1 秒钟超过 1 次查询)时,将会执行 直接限流,效果为 快速失败(会显示默认错误)。
- 【设置流控规则如下:】
- 资源名: /testSentinel/hello
- 针对来源: default
- 阈值类型: QPS
- 单机阈值: 1
- 流控模式: 直接
- 流控效果: 快速失败
如下图所示,1 秒刷新一次是正常返回的结果,而 1 秒刷新多次后,将会输出默认的错误信息。
(4)演示 — 关联、快速失败。
- 【说明:】
- 现有资源 A、B,A 为 /testSentinel/hello,B 为 /testSentinel/relation。
- 配置 A 的流控规则,不区分请求来源,将 A 关联 B。
- 当 B 的 QPS 超过 1(即 1 秒钟超过 1 次查询)时,A 将会被限流,效果为 快速失败(会显示默认错误)。
- 【设置流控规则如下:】
- 资源名: /testSentinel/hello
- 针对来源: default
- 阈值类型: QPS
- 单机阈值: 1
- 流控模式: 关联
- 关联资源: /testSentinel/relation
- 流控效果: 快速失败
- 【实际使用场景举例:】
- 两个资源之间具有依赖关系或者竞争资源时,可以使用关联。
- 比如:
- 对数据库 读操作、写操作 进行限制,可以设置写操作优先,当 写操作 过于频繁时,读操作将被限流。
如下图所示,正常访问 A 是没问题的,但是 B 在 1 秒内多次刷新后,A 将会输出默认出错信息。
(5)演示 — 直接、Wram Up。
- 【说明:】
- 配置 /testSentinel/hello 的流控规则,不区分请求来源,
- 设置 QPS 为 6,预热时间为 3 秒,则开始 QPS 阈值将为 2,预热时间结束后,QPS 会恢复到 6。
- 【设置流控规则如下:】
- 资源名: /testSentinel/hello
- 针对来源: default
- 阈值类型: QPS
- 单机阈值: 6
- 流控模式: 直接
- 流控效果: Warm Up
- 预热时长: 3
- 【实际场景举例:】
- 秒杀系统开启瞬间会有很多请求进行访问,如果不做限制,可能一下子系统直接崩溃了。
- 采用 Warm Up 方式,给系统一个缓冲时间,慢慢的增大 QPS。
如下图所示,开始 QPS 较小,刷新容易报错,3 秒后,QPS 恢复原值,刷新不容易报错。
(6)演示 — 直接、排队等待
- 【说明:】
- 配置 /testSentinel/hello 的流控规则,不区分请求来源,
- QPS 超过 1 时,请求将会排队等待,超时时间为 2 秒,超时后将会输出错误信息。
- 【设置流控规则如下:】
- 资源名: /testSentinel/hello
- 针对来源: default
- 阈值类型: QPS
- 单机阈值: 1
- 流控模式: 直接
- 流控效果: 排队等待
- 超时时间: 2000
- 【实际场景举例:】
- 通常用于处理 间隔性请求。
- 比如:
- 消息队列,某瞬间的请求很多,但之后却没有请求,此时可以使用排队等待,将请求延迟执行(而不是直接拒绝)。
如下图所示,快速刷新页面时,请求将会排队等待执行,超时后将会报错。
4、@SentinelResource 注解
(1)@SentinelResource 注解
@SentinelResource 可以等同于 Hystrix 中的 @HystrixCommand 注解进行理解。
- 【相关地址:】
- https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
- 【@SentinelResource:】
- @SentinelResource 注解用于定义资源,并提供了可选的异常处理 以及 fallback 配置项。
- value:资源名称,必须项(不能为空)。
- blockHandler:
- 指定限流异常(BlockException)发生后,应该执行的方法。
- 注意事项:
- 方法访问权限修饰符为 public。
- 返回值类型 与 原方法返回值类型一致。
- 参数类型 与 原方法一致,并追加一个 额外参数,参数类型为 BlockException。
- blockHandler 指定的函数默认需要与 原方法在同一个类中。
- blockHandlerClass:
- 指定限流异常(BlockException)发生后,应该执行的方法(此方法可以位于 其他类)。
- 注意事项:
- 方法访问权限修饰符为 public。
- 方法必须是 static 函数,否则无法解析。
- fallback:
- 指定异常(除了 exceptionsToIgnore 指定的异常外的异常)发生后,应该执行的方法。
- 注意事项:
- 返回值类型 与 原方法返回值类型一致。
- 参数类型 与 原方法一致,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
- fallback 指定的函数默认需要与 原方法在同一个类中。
- fallbackClass:
- 指定异常发生后,应该执行的方法(此方法可以位于 其他类)。
- 注意事项:
- 方法访问权限修饰符为 public。
- 方法必须是 static 函数,否则无法解析。
- defaultFallback:
- 指定异常发生后,执行默认的逻辑(即 通用处理逻辑)。
- 与 fallback 类似,但 其指定的方法参数为空,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
- 当 fallback 与 defaultFallback 同时存在时,只有 fallback 会生效。
- exceptionsToIgnore:
- 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中(直接对外抛出原异常)。
- 【@SentinelResource 使用注意事项:】
- 若 blockHandler 和 fallback 都配置了,当限流降级异常发生时(即 抛出 BlockException),只会执行 blockHandler 指定的方法。
- 若未配置 fallback、blockHandler 时,则限流降级时,则可能直接抛出 BlockException 异常,若方法本身没定义 throws BlockException,则异常将会被 JVM 包装为 UndeclaredThrowableException 异常。
- 可以简单的理解为:
- blockHandler 用于指定 限流、降级 等异常(BlockException)发生后应该执行的规则。
- fallback 用于指定 其他异常(比如: RuntimeException)发生后应该执行的规则。
(2)@SentinelResource 使用举例
- 【说明:】
- 在 controller 中新增如下代码,
- fallback() 表示异常发生后的回调函数。
- defaultFallback() 表示异常发生后默认的回调函数。
- blockHandler()、blockHandler2() 表示 流控、降级 等 BlockException 异常发生后的回调函数。
- testBlockHandler() 用于测试 blockHandler 参数。
- testFallback() 用于测试 fallback 参数。
- testDefaultFallback() 用于测试 defaultFallback 参数。
- 【新增代码:】
- public String fallback(Integer id, Throwable ex) {
- return ex.getMessage();
- }
- public String defaultFallback(Throwable ex) {
- return ex.getMessage();
- }
- public String blockHandler(BlockException ex) {
- return "block Handler";
- }
- public String blockHandler2(Integer id, BlockException ex) {
- return "block Handler --------- 2";
- }
- @GetMapping("/testBlockHandler")
- @SentinelResource(value = "testBlockHandler", blockHandler = "blockHandler")
- public String testBlockHandler() {
- return "ok";
- }
- @GetMapping("/testFallback/{id}")
- @SentinelResource(value = "testFallback", blockHandler = "blockHandler", fallback = "fallback")
- public String testFallback(@PathVariable Integer id) {
- if (id > 10) {
- throw new RuntimeException("fallback");
- }
- return "ok";
- }
- @GetMapping("/testDefaultFallback/{id}")
- @SentinelResource(value = "testDefaultFallback", blockHandler = "blockHandler2", defaultFallback = "defaultFallback")
- public String testDefaultFallback(@PathVariable Integer id) {
- if (id > 10) {
- throw new RuntimeException("defaultFallback");
- }
- return "ok";
- }
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 回调函数参数 与 原方法相同,可以调用成功。
5、Sentinel Dashboard 使用 — 降级规则、系统规则(系统自适应限流)
(1)熔断降级
熔断降级相关概念,前面在 Hystrix 已经介绍了。
此处仅演示 Sentinel 降级操作。
- 【降级策略:】
- 慢调用比例(SLOW_REQUEST_RATIO):
- 若选择 慢调用比例 作为阈值,需外同时设置几个参数。
- 参数:
- 慢调用最大响应时间(最大 RT)。当请求响应时间大于该值时,将被统计为慢调用。
- 比例阈值。比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。当慢调用比例大于该值时,将会触发熔断机制。
- 最小请求数。单位统计时长内接收请求的最小数。
- 熔断时长。熔断执行的时间。
- 简单解释:
- 当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 慢调用比例(超时请求占总请求数的比例) 大于 比例阈值 时,
- 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
- 熔断时长结束后,会进入探测恢复状态(即 Hystrix 中提到的 HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,若依旧超时,则再次熔断。
- 注:
- 此处的 HALF-OPEN 状态,来源于官网介绍(针对 Sentinel 1.8.0 及以上版本)。
- 旧版本可能没有 HALF-OPEN 状态(没实际验证过)。
- 异常比例(ERROR_RATIO):
- 异常比例 与 慢调用比例 类似,异常比例的参数少了个 最大响应时间。
- 简单解释:
- 当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 异常比例(异常请求占总请求数的比例) 大于 比例阈值 时,
- 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
- 熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。
- 异常数(ERROR_COUNT):
- 异常数 与 异常比例 类似,异常数 将参数 异常比例 变为 异常数(直接监控异常数,而非比例)。
- 简单解释:
- 当单位统计时长(statIntervalMs)内 异常请求数 超过 异常数阈值 后,
- 将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
- 熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。
(2)演示 — 异常比例
在上面 testDefaultFallback() 基础上,添加 降级 规则,演示 异常比例 降级。
注:
删除添加的流控规则,并指定 降级规则。
当降级发生时,将会触发 blockHandler 指定的回调方法。
(3)系统规则
- 【相关文档:】
- 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
- 【什么是系统规则(系统自适应限流):】
- 系统自适应限流 是从 整体维度 对 应用程序 入口流量 进行控制,即 在调用应用程序的 方法(接口) 前,将请求拦截下来。
- 通过自适应的流控策略,让 系统的入口流量 和 系统的负载 达到一个平衡,即 让系统尽可能 在保证 最大吞吐量的同时 保证 系统整体的稳定性,
- 常用指标:
- 应用的负载(Load)、
- CPU 使用率、
- 总体平均响应时间(RT)、
- 入口 QPS、
- 并发线程数 等。
6、OpenFeign 整合 Sentinel(引入 sentinel 依赖需要注意版本问题)
(1)说明
- 【说明:】
- OpenFeign 一般用于消费端,此处以 eureka_client_consumer_9001 为基础,整合 Sentinel。
- 注:
- 此处为了省事,直接用之前创建好的子模块,亦可自行创建新的模块。
- 使用流程 与 Hystrix 类似。
(2)整合 Sentinel
Step1:
在 eureka_client_consumer_9001 基础上引入 依赖。
- 【依赖:】
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
- <!-- alibaba-sentinel -->
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- <version>2.2.1.RELEASE</version>
- <exclusions>
- <exclusion>
- <groupId>com.fasterxml.jackson.dataformat</groupId>
- <artifactId>jackson-dataformat-xml</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- 【注意事项:(巨坑)】
- alibaba-sentinel 的版本可能会影响程序的执行。
- 此处使用的版本:
- springcloud Hoxton.SR9
- spring.cloud.alibaba 2.1.0.RELEASE
- springboot 2.3.5.RELEASE
- 引入 alibaba-sentinel 依赖后,服务一直无法启动。
- 报错:
- 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.
- 具体原因没整明白,但是如上引入依赖后,可以解决问题(有时间再去研究)。
Step2:
修改配置文件。开启 Sentinel 对 Feign 的支持。
- 【application.yml】
- # 开启 sentinel 对 feign 的支持
- feign:
- sentinel:
- enabled: true
Step3:
使用 @FeignClient 编写服务调用。
- 【ProducerFeignService】
- package com.lyh.springcloud.eureka_client_consumer_9001.service;
- import com.lyh.springcloud.eureka_client_consumer_9001.service.impl.ProducerFeignServiceImpl;
- import org.springframework.cloud.openfeign.FeignClient;
- import org.springframework.stereotype.Component;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- @FeignClient(value = "EUREKA-CLIENT-SENTINEL-PRODUCER-8010", fallback = ProducerFeignServiceImpl.class)
- @Component
- public interface ProducerFeignService {
- @GetMapping("/testSentinel/testDefaultFallback/{id}")
- String testDefaultFallback(@PathVariable Integer id);
- @GetMapping("/testSentinel/hello")
- String hello();
- }
- 【ProducerFeignServiceImpl】
- package com.lyh.springcloud.eureka_client_consumer_9001.service.impl;
- import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
- import org.springframework.stereotype.Component;
- @Component
- public class ProducerFeignServiceImpl implements ProducerFeignService {
- @Override
- public String testDefaultFallback(Integer id) {
- return "系统异常,请稍后重试 --------- 1111111111111";
- }
- @Override
- public String hello() {
- return "系统异常,请稍后重试 --------- 2222222222222";
- }
- }
Step4:
编写 controller。
- 【TestController】
- package com.lyh.springcloud.eureka_client_consumer_9001.controller;
- import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- @RequestMapping("/consumer2")
- @RestController
- public class TestController {
- @Autowired
- private ProducerFeignService producerFeignService;
- @GetMapping("/testDefaultFallback/{id}")
- public String testDefaultFallback(@PathVariable Integer id) {
- return producerFeignService.testDefaultFallback(id);
- }
- @GetMapping("/hello")
- public String hello() {
- return producerFeignService.hello();
- }
- }
Step5:
在启动类上添加 @EnableFeignClients 注解,开启 feign 功能。
Step6:
测试。
给 testDefaultFallback() 添加流控规则,QPS 大于 1 时将限流。
QPS 小于等于 1 时,正常访问。
若远程服务断开后,访问 testDefaultFallback() 将失败,从而执行本地添加的逻辑。
7、持久化配置信息到 Nacos
后续使用到 Nacos 再介绍,此处暂时省略。。。