Sentinel实现熔断与限流
官网:https://github.com/alibaba/Sentinel/wiki
是什么?
Sentinel是把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Hystrix的不足
- 需要自己手工搭建监控平台
没有一套web界面可以给我们进行更加细粒度化的配置。流控、速率控制、服务熔断、服务降级…
安装
- 下载jar 运行java -jar
- 默认端口8080
--server.port=8081
更改端口
Sentinel分为两个部分
核心库(java客户端):不依赖任何框架/库,能够运行与所有的java运行环境,同时对Dubbo、SpringCloud 等框架也有较好的支持。
控制台(Dashboard):基于SpringBoot开发,打包后可以直接运行。
操作:
- 启动Nacos8848
- 新建cloudalibaba-sentinel-service8401,注册进Nacos,同时被 Sentinel监控
Pom
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
yaml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置sentine dashboard地址
dashboard: localhost:8080
# dashboard: 119.3.160.59:8858
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口。
port: 8719
management:
endpoints:
web:
exposure:
include: "*"
Controller
@RestController
public class FlowLimitController {
@GetMapping("testA")
public String testA(){
return "----testa";
}
流量控制规则
QPS:每秒请求数。
线程数:当调用该api线程数达到阈值的时候,进行限流。(使用多个窗口访问测试)
流控模式
- 直接:当api达到限流条件时,直接限流。
- 关联:当关联的资源达到阈值时,就限流自己。
- 使用TestA关联testB阈值设为1,使用postman多线程集合组访问testB,此时TestB访问达到阈值,testA也不能访问。
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)
流控效果
快速失败:直接失败,抛异常。调用
package com.alibaba.csp.sentinel.slots.block.flow.controller;
- Warm Up:根据orderFactor(冷加载因子,默认3),从阀值/codeFactor进过预热时长,才达到设置的QPS阀值。 调用
package com.alibaba.csp.sentinel.slots.block.flow.controller;
- RT为相应时间,慢调用比例为超出设置响应时间比例,则开启熔断,熔断时长过了就关闭熔断。
- 异常比例: 只需要设置异常比例一个参数0.1~1.0。达到最小请求数,且比例达到,进行熔断。
- 异常数: 只需要设置异常数,达到最小请求数,且异常数达到,进行熔断。
热点Key限流
是什么?
热点就是经常访问的数据。比如某个访问频率Top的数据。比如:某个商品id,某个用户id。原理
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。基本使用
@GetMapping("/testHotkey") @SentinelResource(value = "testHotkey",blockHandler = "deal_testHotkey")//value为sentinel识别唯一标识,blockHandler为兜底解决方案 public String testHotKey(@RequestParam(value = "p1",required = false) String p1, @RequestParam(value = "p2",required = false) String p2){ return "=========testHotKey"; } public String deal_testHotkey(String p1, String p2, BlockException e){ return "########deal_testHotkey,o(╥﹏╥)o"; }
- value为sentinel识别唯一标识
- blockHandler为兜底解决方案
参数例外项
即在热点限流的基础上,可以添加参数例外项,比如参数为VIP,设置另外的限流阈值。(万恶的VIP用户)
总结:
@SentinelResource
只管配置出错,运行出错走异常系统保护规则
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
客户自定义限流处理逻辑
创建CustomerBlockHandler类用于自定义限流处理逻辑
创建自定义限流处理类:CustomerBlockHandler
public class CustomerBlockHandler { //必须使用static public static CommonResult handlerException(BlockException e){ return new CommonResult(444,"按客户自定义,global,handlerException----1"); } public static CommonResult handlerException2(BlockException e){ return new CommonResult(444,"按客户自定义,global,handlerException----2"); } }
RateLimitController
@GetMapping("/ratelimit/CustomerBlockHandler") @SentinelResource(value = "CustomerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2") public CommonResult CustomerBlockHandler(){ return new CommonResult(200,"按客户自定义限流测试ok","serial003"); }
blockHandlerClass:表示统一处理的类。
- blockHandler:指定具体的方法
服务熔断功能
Sentinel整合了Ribbon+OpenFeign+fallback
Ribbon+fallback案例
- 启动
nacos8848
和Sentinel8888
新建微服务提供者
cloudalibaba-provider-payment9003/9004
@RestController public class PaymentController { @Value("${server.port}") private String serverPort; public static HashMap<Long, Payment> hashmap=new HashMap<>(); static { hashmap.put(1L,new Payment(1L,"111111111111111")); hashmap.put(2L,new Payment(2L,"222222222222222")); hashmap.put(3L,new Payment(3L,"333333333333333")); } @GetMapping("/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id")Long id){ Payment payment = hashmap.get(id); return new CommonResult<>(200,"from sql,serverport"+serverPort,payment); } }
新建微服务消费者
cloudalibaba-consumer-nacos-order84
@RestController @Slf4j public class CircleBreakerController { public static final String SERVICE_URL="http://nacos-payment-provider"; @Resource private RestTemplate restTemplate; @GetMapping("/consumer/fallback/{id}") // @SentinelResource(value = "fallback") // @SentinelResource(value = "fallback",fallbackClass = CustomerBlockHandler.class, fallback = "handlerFallback") @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) public CommonResult<Payment> fallback(@PathVariable long id){ CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class,id); if (id==4){ throw new IllegalArgumentException("IllegalArgumentException,非法参数异常"); }else if(result.getData()==null){ throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常"); } return result; } public CommonResult<Payment> handlerFallback(@PathVariable long id,Throwable throwable){ Payment payment = new Payment(id, null); return new CommonResult<>(444,"兜底异常handlerFallback,exeception内容"+throwable.getMessage(),payment); } public CommonResult<Payment> blockHandler(@PathVariable long id, BlockException blockException){ Payment payment = new Payment(id, null); return new CommonResult<>(445,"blockHandler限流,无此流水"+blockException.getMessage(),payment); } }
使用order84访问服务提供者9003/9004,传参1,2,3为正确参数,其余为异常。
- 正确参数,9003和9004轮询访问,实现了负载均衡。
- 错误参数,返回报错页面,不友好。
- 配置Sentinel限流
fallback
,只会再达到限流条件时返回限流结果。 - 同时配置Sentinel熔断
blockHandler
,报错返回熔断结果,达到限流条件返回限流结果。
- 配置Sentinel限流
- 结论:fallback管运行时异常; BlockHandler管配置违规。
配置exceptionsToIgnore,会忽略代码中某个异常处理。
OpenFeign案例
在服务端order84创建OpenFeignService模块
pom
<!--openFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
yaml
#集合sentinel对feign的支持 feign: sentinel: enabled: true
主启动 @EnableFeignClients //启用openfeign
业务类Service(远程调用)、ServiceImpl(降级容错):
//远程调用 @FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackServiceImlp.class) public interface PaymentService { @GetMapping("/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id")Long id); } //降级容错 //服务降级返回 @Component public class PaymentFallbackServiceImlp implements PaymentService{ @Override public CommonResult<Payment> paymentSQL(Long id) { return new CommonResult<>(444,"服务降级返回,---PaymentFallbackService", new Payment(id,"error")); } }
Controller
@RestController public class OpenFeignController { @Resource private PaymentService paymentService; @GetMapping("/consumer/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) { CommonResult<Payment> paymentCommonResult = paymentService.paymentSQL(id); return paymentCommonResult; }
熔断框架比较
Sentinel持久化规则
存在的问题
解决办法
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的限流规则就持续有效。
操作:
修改8401
pom
<!--sentinel持久化--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
yaml ```yaml server: port: 8401
spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport:
#配置sentine dashboard地址
dashboard: localhost:8080
dashboard: 119.3.160.59:8858
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口。
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management: endpoints: web: exposure: include: “*”
2. 新建Nacos配置:
1. `DataID:cloudalibaba-sentinel-service`
1. 配置格式:json
```json
[{
"resource":"/rateLimit/byUrl",
"limitApp":"default",
"grade":1,
"conut":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}]
- 刷新8401发现sentinel中已经存在业务规则。关闭8401发现sentinel中配置消失了,重启后访问8401,sentinel重新出现,持久化配置成功。