SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)
Posted ABin-阿斌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)相关的知识,希望对你有一定的参考价值。
- 声明:原文作者:csdn:yuan_404
文章目录
1 . 降级规则
1.1 介绍
- 慢调用比例 (SLOW_REQUEST_RATIO):
- 选择以慢调用比例作为阈值,需要设置允许的 慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。
- 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。
- 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)
- 若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (ERROR_RATIO):
- 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。
- 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- 异常数 (ERROR_COUNT):
- 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
1.2 慢调用比例(RT)
-
先在 Controller 中添加一个方法
@GetMapping("/testD") public String testD() try TimeUnit.SECONDS.sleep(1); catch (InterruptedException e) e.printStackTrace(); log.info("testD 测试RT"); return "------testD";
-
设置降级规则
-
使用 JMeter 进行压测
-
启动 JMeter ,去浏览器上访问该请求,查看结果
-
停止 JMeter 后,再访问该请求
1.3 异常比例
-
修改 Controller ,添加一个运行时异常
-
修改 降级规则
-
启动 JMeter ,去浏览器上访问该请求,查看结果
-
停止 JMeter 后,再访问该请求
1.4 异常数
-
修改 降级规则
-
连续发出请求,当刷新第六次的时候被熔断了
-
过一段时间后再次访问
2 . @SentinelResource 详解
2.1 按资源名称限流 + 备选方案
- 启动 Nacos 控制台
- 启动 Sentinel 控制台
2.1.1 环境搭建
-
修改模块 :8401
-
修改 POM
<dependency> <groupId>com.demo.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>$project.version</version> </dependency>
-
新建 Controller :RateLimitController
@RestController public class RateLimitController @GetMapping("/byResource") @SentinelResource(value = "byResource",blockHandler = "handleException") public CommonResult byResource() return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001")); public CommonResult handleException(BlockException exception) return new CommonResult(444,exception.getClass().getCanonicalName()+"\\t 服务不可用");
-
启动项目
2.1.2 配置流控规则
- 当每秒访问该资源的请求数大于 1,则限流
2.1.3 额外问题
- 这时突然关闭 8401 服务,流控规则是否会消失?
2.2 按照Url地址限流 + 备选方案
-
修改 RateLimitController
@GetMapping("/rateLimit/byUrl") @SentinelResource(value = "byUrl") public CommonResult byUrl() return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
在 Controller 中添加这样一个方法
没有配置 blockHandler 限流之后的处理方法
-
启动 8401
-
配置流控规则
-
这里是将请求的 Url 作为限流的资源名
2.3 上面 两个配置备选方案的方式 的问题
-
系统默认的,没有体现我们自己的业务要求。
-
依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
-
每个业务方法都添加一个兜底的,那代码膨胀加剧。
-
全局统—的处理方法没有体现。
- 下面就来尝试解决一下这些 代码耦合、膨胀的问题
2.4 自定义限流处理逻辑
- 修改 alibaba-sentinel-service-8401
-
自定义一个限流处理类 :CustomerBlockHandler
public class CustomerBlockHandler public static CommonResult handleException(BlockException exception) return new CommonResult(2020, "自定义限流处理信息....CustomerBlockHandler");
-
修改 Controller :RateLimitController
@GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2") public CommonResult customerBlockHandler() return new CommonResult(200,"客戶自定义 Handler",new Payment(2020L,"serial003"));
-
重启 8401
-
配置限流规则
2.5 @SentinelResource 注解属性说明
-
value:资源名称,必需项(不能为空)
-
entryType:entry 类型,可选项(默认为 EntryType.OUT)
-
blockHandler: 处理BlockException的函数名称。函数要求:
1、必须是 public
2、返回类型与原方法一致
3、参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。
4、默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。 -
blockHandlerClass :存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。
-
fallback :fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
1、返回值类型必须与原函数返回值类型一致;
2、方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
3、fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 -
exceptionsToTrace :需要trace的异常
3 . 服务熔断功能
- 先启动 Nacos 和 Sentinel
3.1 Ribbon系列
- 避免和前面的知识点混淆,这里重新创建一套环境
3.1.1 提供者 Provider-9003
-
新建模块 :alibaba-provider-Sentinel-Ribbon-9003
-
修改 POM
<dependencies> <!--SpringCloud ailibaba nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <groupId>com.demo.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>$project.version</version> </dependency> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--日常通用jar包配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
编写 YML
server: port: 9003 spring: application: name: nacos-payment-provider cloud: nacos: discovery: server-addr: localhost:8848 #配置Nacos地址 management: endpoints: web: exposure: include: '*'
-
编写主启动类
@SpringBootApplication @EnableDiscoveryClient public class ProviderMain9003 public static void main(String[] args) SpringApplication.run(ProviderMain9003.class, args);
-
业务类
-
Controller
@RestController public class ProviderController @Value("$server.port") private String serverPort; public static HashMap<Long, Payment> hashMap = new HashMap<>(); static hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181")); hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182")); hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183")); @GetMapping(value = "/paymentSQL/id") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) Payment payment = hashMap.get(id); CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment); return result;
-
测试
启动 Sentinel、Nacos
启动 Provider-9003
访问 :http://localhost:9003/paymentSQL/1
-
Provider-9003 搭建成功,复制配置,克隆出来一个 9004,具体操作之前有说过这就不多说了。
3.1.2 消费者 Consumer-84
-
新建模块 :alibaba-consumer-Sentinel-Ribbon-84
-
修改 POM
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.demo.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>$project.version</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
编写 YML
server: port: 84 spring: application: name: nacos-order-consumer cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719 service-url: nacos-user-service: http://nacos-payment-provider
-
编写主启动类
@SpringBootApplication @EnableDiscoveryClient public class ConsumerMain84 public static void main(String[] args) SpringApplication.run(ConsumerMain84.class, args);
-
业务类
-
Config :ApplicationContextConfig
@Configuration public class ApplicationContextConfig @Bean @LoadBalanced public RestTemplate getRestTemplate() return new RestTemplate();
-
Controller
@RestController public class CircleBreakerController @Value("$service-url.nacos-user-service") private String SERVER_URL; @Autowired private RestTemplate restTemplate; @GetMapping(value = "/consumer/paymentSQL/id") @SentinelResource(value = "fallback") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) return restTemplate.getForObject(SERVER_URL+"/paymentSQL/"+id, CommonResult.class);
3.1.3 熔断降级配置
- 注意 :
- 热部署对java代码级生效及时
- 对 @SentinelResource 注解内属性,有时效果不好
- 所以需改了该注解中的内容,最好还是重启
-
在之前的 Consumer 的 Controller 中添加一段异常捕获的代码
// 异常捕捉 if (id == 4) throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常...."); else if (result.getData() == null) throw new SpringCloud-2.0-周阳(24. 分布式事务 - Seata)
SpringCloud-2.0-周阳(17. SpringCloud Alibaba入门简介)
SpringCloud-2.0-周阳(14. 消息总线 - SpringCloud Bus)
SpringCloud-2.0-周阳(21. Sentinel 环境搭建)