Sentinel前置
- 官网:https://github.com/alibaba/Sentinel/wiki,分布式的流量防卫兵,轻量级的流量控制、断熔降级JAVA库;
-
How to use? https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
-
粗略的说,可以应对服务使用中的以下问题:服务雪崩、服务降级、服务熔断、服务限流;
-
Sentinel组件由两部分组成,分别是后台,和前台8080;
- 核心库(java客户端),不依赖任何框架和库,能够运行于所有java运行时的环境,同时对Dubbo,Spring Cloud等框架也有友好支持
- 控制台(Dashboard),基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器;
-
下载和安装:
- 哪里下载:https://github.com/alibaba/Sentinel/releases
- 运行:
- 前提1:java8环境OK;
- 前提2:8080端口不能被占用; 可以打开cmd,输入命令进行查看: netstat -aon|findstr “8080”
- 到下载好的目录下,运行命令:java -jar sentinel-dashboard-1.7.0.jar
- 访问sentinel管理界面:
- http://localhost:8080
- 账号密码均为:sentinel
版本说明
- Sentinel 1.7.0
Sentinel初始化演示工程
-
启动Nacos8848成功,这里我用上一章搭好的nacos集群,学以致用一下;
-
创建Module;
-
cloudalibaba-sentinel-service8401
-
修改POM,创建YML,
-
#application.yml server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: #nacos集群服务注册中心地址 server-addr: 192.168.56.128:1111 sentinel: transport: #配置Sentinel dashboard地址 dashboard: localhost:8080 #默认8719端口,假如被占用,会自动从8719开始,依次+1扫描,直到找到未被占用的端口 port: 8719 management: endpoints: web: exposure: include: '*'
-
-
主启动类,了解注解@EnableDiscoveryClient
-
controller:
-
package com.atguigu.springcloud.alibaba.controller; @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ return "---------testA"; } @GetMapping("/testB") public String testB(){ return "---------testB"; } }
-
-
-
启动Sentinel8080,java -jar sentinel-dashboard-1.7.0.jar;
-
启动微服务8401,后台(localhost:8080),查看;
-
发现后台什么都没有,这是因为sentinel采用的是懒得加载机制,需要执行一次访问后,才能看到效果,试着先访问:http://localhost:8401/testA或testB,再去后台查看,即可看到一个微服务的监控等功能列表;
Sentinel流控规则简介
- 解释说明:
- 资源名:唯一名称,默认是请求路径;
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名称,默认是default(不区分来源);
- 阈值类型/单击阈值:
- QPS(每秒请求数):当调用该api的QPS达到阈值,进行限流;
- 线程数:当调用该api的线程数达到阈值时,进行限流;
- 是否集群(sentinel集群):不需要集群;
- 流控模式:
- 直接:api达到限流条件时,直接限流;
- 关联:当关联的资源达到阈值时,就限流自己;
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 控流效果:
- 快速失败:直接失败抛出异常;
- Warm up:根据codeFactor(冷加载因子,默认是3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值;
- 排队等待:匀速派对,让请求以匀速的速度通过,阈值类型必须设置未QPS,否则无效;
创建流控规则,可以从流控规则功能栏中添加,也可以到簇点链路中直接添加流控,一般是到簇点链路中;
- 案例1:阈值类型——QPS直接失败
-
注意资源名,即之前的localhost:8401不在范围内;
-
这时的效果是,访问资源/testA,必须是一秒内只能有1个访问量,否则会在网页上报错:Blocked by Sentinel (flow limiting);
-
这里报错时,直接调用的是默认的报错信息,技术方面是可以的,但是如何添加自己的后续处理呢?类似有一个fallback的兜底方法?
- 有的,后面说;
-
案例2:阈值类型——线程数直接失败
-
线程数的意思是,每一次能处理的并发量,同一时间能处理的请求数;
-
如何测试:可以设置线程数为1,然后在8401module中的controllelr中的/testA请求中,添加睡眠时间。然后访问/testA时狂点,报错Blocked by Sentinel (flow limiting);
@GetMapping("/testA") public String testA(){ try { TimeUnit.MILLISECONDS.sleep(800); }catch (InterruptedException e){ e.printStackTrace(); } return "---------testA"; }
-
-
案例3:流控模式——关联
-
当关联的资源达到阈值时,就限流自己;
-
即当与A关联的资源B达到阈值后,就限流A自己;即在同一个的controller中的rest地址,例如testA和testB。B惹事,A挂了。
-
应用:例如支付接口达到阈值时,就限流下订单的接口;
-
-
当关联资源testB的QPS超过1时,限流testA;
-
当关联资源到达阈值后,限流配置好的资源名;
-
测试:
- 用postman模拟并发密集的访问(20次迭代,间隔0.3秒执行一次访问)testB,然后观察/testA,报错;
-
案例4:流控模式——链路
- 只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 解释:
-
假设:请求接口/testA会调用service层的getOrder方法,/testB方法也会调用service层的getOrder方法;
-
此时如果/testA访问超过阈值,会进行流量控制;但/testB则不会。
-
-
案例5:流控效果——预热
- 根据codeFactor(冷加载因子,默认是3)的值,从(阈值/codeFactor),经过预热时长,才逐渐达到设置的QPS阈值;
- 公式:阈值除以coldFactor(默认为3),经过预热时长后才会达到阈值;
- 当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位,可能瞬间把系统压垮。通过“冷启动”,让流量缓慢增加,在一定时间逐渐增加到阈值上限;
-
案例6:流控效果——排队等待
- 匀速排队,让请求以均匀的速度通过,阈值必须设置为QPS,否则无效;
- 例如QPS阈值为1,则含义是/testA每秒请求1次,超过的话就排队等待,等待的超时时间自己设置;
Sentinel降级
-
上述界面基本介绍
- RT(平均响应时间,秒级):平均响应时间(例如1秒内进入5个请求),计算出平均响应时间。若超出阈值,且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级,窗口期过了之后关闭降级,RT最大4900(大概5秒内),更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
- 异常比例(秒级):QPS>=5,且异常比例(秒级统计)超过阈值时,触发降级:时间窗口结束后,关闭降级;
- 异常数(分钟级):异常数(分钟统计),超过阈值时,触发降级,时间窗口结束后,关闭降级;
-
Sentinel熔断降级会在调用链路中,某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
-
Sentinel的断路器是没有半开状态的。所谓半开状态,就是半开状态的系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器(不可用状态)。Sentinel是时间窗口到了,就关闭断路器恢复使用。
-
案例1:Sentinel降级——RT
-
案例2:Sentinel降级——异常比例
-
异常比例:当资源的每秒请求量>=5,并且每秒异常总数占用通过量的比值超过阈值之后,资源进入降级状态。在接下来的时间窗口之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0%~100%;
-
在FlowLimitController下添加如下代码:
@GetMapping("/testF") public String testF(){ log.info("testF 异常比例"); int age = 10/0; return "---------testF"; }
-
开启JMETER后,直接高并发发送请求,多次调用达到熔断条件,服务降级。断路器开启(保险丝跳闸),微服务不可用了,不再报错error,而是服务降级了。
-
-
案例3:Sentinel降级——异常数
- 异常数:当资源近1分钟的异常数目超过阈值之后,会进行熔断。注意由于统计时间是分钟级别的,若timewindow小于60s,则结束熔断状态后仍可能再进入熔断状态。
Sentinel热点Key
-
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的Top K数据,并对其访问进行限制。
- 例如:商品ID为参数,统计一段时间内最常购买的商品ID,并进行限制;用户ID为参数,针对一段时间内频繁访问的用户ID进行限制;
- 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看作是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
-
热点限流
- 在controller中添加如下代码:
@GetMapping("/testHotKey") @SentinelResource(value="testHotKey",blockHandler = "deal_testHotKey")//这里value是什么都行,这里只是为了好看 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 exception){ return "--------deal_testHotKey,我是兜底备胎降级函数,错错错错错错";//代替系统默认提示:Blocked by Sentinel(flow limiting) }
-
注意:这里资源名同代码中的value值,不带/。
-
上面的单击阈值是QPS模式,所以是1秒一次的含义;参数索引0代表是第一个参数;
-
上面的单击阈值是QPS模式,所以是1秒一次的含义;参数索引0代表是第一个参数;
-
两种情况:
- 普通:超过1秒种后,达到阈值1后马上被限流;
- 特殊:我们期望p1参数当是某个特殊值时,它的限流值和平时不同;
-
可以设置当参数p1为某个特定值时,其阈值也会变为特殊的值;
-
注意:
- @SentinelResource处理的时Sentinel的控制台配置的违规情况,有blockHandler方法配置的兜底处理;
- 但是如果此时是代码出了问题(如10/0),则这个时java运行时产生的异常,不归@SentinelResource管。
- 因此@SentinelResource主管配置出错,运行出错则该走异常就走异常。
Sentinel系统自适应限流
-
Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU使用率、总体平均RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
-
支持的模式:
- Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统的保护。当系统load1超过设定的启发值,且系统当前的并发线程数超过估值的系统容量时才会触发系统保护;
- CPU usage:当系统CPU使用率超过阈值,即触发系统保护;
- 平均RT:当单台机器所有入口流量的平均RT达到阈值,即触发系统保护,单位是毫秒;
- 并发线程数:当单台机器所有入口流量的并发线程数达到阈值,即触发系统保护;
- 入口QPS:当单台机器所有入口流量的QPS达到阈值,即触发系统保护。
- 较为危险的方法
@SentinelResource的配置方法
-
按资源名称限流+后续处理:
-
启动nacos;启动sentinel;修改(cloudalibaba-sentinel-service8401),新写一个业务类RateLimitController
@RestController public class RateLimitController { @GetMapping("/byResource") @SentinelResource(value = "byResource",blockHandler = "handlerException") public CommonResult byResource() { return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001")); } public CommonResult handlerException(BlockException exception) { return new CommonResult(444,exception.getClass().getCanonicalName()); } }
-
对资源byResource设置流控,QPS阈值为1;
-
关闭8401微服务后,流控规则也会跟着消失,因此是临时的。
-
-
按照Url地址限流+后续处理
-
通过访问URL来限流,会返回Sentinel自带默认的限流处理信息;
-
往业务类RateLimitController添加相关代码;
@GetMapping("/rateLimit/byUrl") @SentinelResource(value="byUrl") public CommonResult byUrl() { return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002")); }
-
访问一次;
-
Sentinel控制台配置,按照Url地址限流;
-
-
测试;
-
客户自定义限流处理逻辑
-
上述用的,一般兜底方法面临的问题:
- 系统默认的兜底方法,没有体现我们自己的业务要求;
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观;
- 每个业务方法都添加一个兜底的,会导致代码膨胀加剧;
- 全局统一的处理方法没有体现;
-
具体步骤:
-
创建CustomerBlockHandler类,用于自定义限流处理逻辑;
public class CustomerBlockHandler { public static CommonResult handlerException(BlockException exception){ return new CommonResult(4444,"按照客户自定义的,global,的方法",new Payment(2020L,"serial003")); } }
-
自定义限流处理类:CustomerBlockHandler;
-
在RateLimitController中,添加如下代码;
@GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException") //即标明,哪个类中的哪个方法是兜底方法 public CommonResult customerBlockHandler(){ return new CommonResult(200,"按照客户自定义的兜底方法",new Payment(2020L,"serial003")); }
-
启动微服务后先调用一次;
-
Sentinel控制台配置;
-
测试自定义的兜底方法;
-
进一步说明问题;
-
-
-
@SentinelResource的其他属性说明
- Sentinel主要有三个核心API:
- SphU定义资源;
- Tracer定义统计;
- ContextUtil定义上下文;
- Sentinel主要有三个核心API:
Sentinel熔断功能
-
sentinel整合了Ribbon(负载均衡)+openFeign( 远程调用)+fallback(降级);( OpenFeign是声明式、模板化的HTTP请求客户端。可以更加便捷、优雅地调用http api);
-
Ribbon系列:
-
启动nacos和sentinel;
-
提供者9003和9004;
-
业务逻辑层代码:
@RestController public class PaymentController { @Value("${server.port}") private String serverPort; public static HashMap<Long, Payment > map = new HashMap<>(); static {//这里当作一个dao层 map.put(1L,new Payment(1L,"1111")); map.put(2L,new Payment(2L,"2222")); map.put(3L,new Payment(3L,"3333")); } @GetMapping(value = "/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) { Payment payment = map.get(id); CommonResult<Payment> result = new CommonResult<>(200,"from mysql,serverPort: " + serverPort,payment); return result; } }
-
-
消费者84,经过ribbon负载均衡,调用9003和9004;
-
注意:修改后请重启微服务,因为热部署对java代码级生效及时,对@SentinelResource注解内属性,有时效果不好;
-
该实验目的,是为了对比fallback和blockHandler的区别:
- fallback管运行异常;
- blockHandler管配置违规;
- 若blockHandler和fallback都进行了配置,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑;
-
服务熔断exceptionsTolgnore,忽略已经在代码中抛出异常的情况;
-
-
Feign系列
- 修改84模块:84消费者调用提供者9003,Feign组件一般用在消费侧;
- 修改POM(添加OpenFeign)、YML(激活sentinel对feign的支持)、业务类、主启动(添加注解@EnableFeignClients)
- 测试,访问http://localhost:84/consumer/paymentSQL/1
- 测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级;
sentinel规则的持久化
-
之前说过,如果一个微服务被关闭了(或者重启),则sentinel中相关的配置也消失了,那么如何使配置规则持久化呢?
-
我们可以将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里的配置不删除,针对8401上的sentinel上的流控规则持续有效;
-
操作:
-
修改8401的POM文件,添加sentinel-datasource-nacos定位,持久化需要用到;
-
添加datasource配置
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: #nacos集群服务注册中心地址 server-addr: 192.168.56.130:1111 sentinel: transport: #配置Sentinel dashboard地址 dashboard: localhost:8080 #默认8719端口,假如被占用,会自动从8719开始,依次+1扫描,直到找到未被占用的端口 port: 8719 datasource: ds1: nacos: server-addr: 192.168.56.130:1111 dataId: cloudalibaba-sentinel-service #这是注册进sentinel的服务名称cloudalibaba-sentinel-service groupId: DEFAULT_GROUP data-type: json rule-type: flow management: endpoints: web: exposure: include: '*'
-
* resource:资源名称
* limitApp:来源应用;
* grade:阈值类型,0表示线程数,1表示QPS
* count:单击阈值
* strategy:流控模式,0表示直接,1表示关联,2表示链路;
* controlBehavior:流控效果,0表示快速失败,1是warm up,2表示排队等待;
* clusterMode:是否集群;
- 实现持久化;
知识点
tips
- Hystrix不足:
- 需要我们程序员自己手工搭建监控平台;
- 没有一套web界面可以给我们进行更细粒度化的配置流控、速率控制、服务熔断、服务降级。。。。
- Sentinel与上面Hystrix的对比:
- 单独一个组件,可以独立出来;
- 直接界面化的细粒度统一配置;
- 约定>配置>编码;上面的流控、降级之列的都可以写在代码里,为了少些代码,可以用配置和注册;
代码地址:https://github.com/AJ-Spade/cloud2020/tree/master