Spring Cloud系列— Alibaba Sentinel限流
上篇文章:
Spring Cloud系列—Config配置中心https://blog.csdn.net/sniper_fandc/article/details/149943840?fromshare=blogdetail&sharetype=blogdetail&sharerId=149943840&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目录
1 熔断和限流
1.1 熔断
1.2 限流
1.3 熔断VS限流
2 Sentinel 简介
3 Sentinel DashBoard
3.1 下载与安装
3.1.1 下载jar包
3.1.2 启动Sentinel DashBoard
3.1.3 访问Sentinel DashBoard客户端
4 限流
4.1 Java实现Sentinel的限流
4.1.1 引入依赖
4.1.2 定义资源
4.1.3 定义限流规则
4.1.4 调用资源
4.2 Spring Cloud集成Sentinel实现限流
4.2.1 引入依赖
4.2.2 添加配置项
4.2.3 Sentinel监控
4.3 Sentinel流量控制
4.3.1 基于QPS/并发线程数的流量控制
4.3.2 流控效果
4.3.3 流控模式
4.3.4 热点参数限流
1 熔断和限流
1.1 熔断
熔断:当微服务系统某个服务出现故障,为了不让故障扩散,调用该故障服务的服务会进行熔断,即断绝该故障服务接口与其它服务之间的联系。在熔断期间,所有请求服务调用者都会得到失败响应,从而防止因为故障服务而导致系统雪崩。
比如某微服务系统,服务1调用服务2,服务2调用服务3,假设服务3故障,此时服务2向服务3发送的请求就会超时,进而服务1向服务2发送的请求也会超时,在高流量的情况下,所有的请求都会等待很长时间然后超时,导致系统资源被耗尽而无法得到有效利用。
1.2 限流
限流:在高流量情况下,系统处理请求的响应时间变长,为了保证用户体验,对请求数限流就可以让每个请求都得到较好的处理,从而提升用户体验。
1.3 熔断VS限流
熔断和限流都是为了保证系统的安全性,即服务保护策略。但是二者存在很大的不同:
(1)从工作场景分析:熔断应对故障,限流应对高负载。
(2)从目的分析:熔断防止故障扩散,限流防止系统过载。
(3)从触发条件分析:服务异常才会触发熔断,请求过多才会触发限流。
(4)从处理方式分析:熔断直接切断服务的请求,限流限制请求的数量。
2 Sentinel 简介
Sentinel是Alibaba开源的面向分布式的流量治理组件,具有丰富的应用场景、便于接入和使用、多样化的流量控制、可视化的监控和规则管理等特性。
Sentinel分为核心库和控制台两部分,核心库就是Java客户端,不依赖库和框架,能运行在任何Java环境中,对Dubbo和Spring Cloud也有较好的支持。控制台(DashBoard)是可视化的监控工具,基于SpringBoot开发,下载jar包打开后即可运行,不需要Tomcat等Web容器。
3 Sentinel DashBoard
3.1 下载与安装
3.1.1 下载jar包
前往Sentinel的Github地址:https://github.com/alibaba/Sentinel/releases,下载jar包。
然后把下载的jar包放到本地目录:
3.1.2 启动Sentinel DashBoard
启动方式是cmd运行命令:
java -jar .\sentinel-dashboard-1.8.8.jar
3.1.3 访问Sentinel DashBoard客户端
输入127.0.0.1:8080(SpringBoot默认启动端口),即可访问客户端:
默认用户名和密码是sentinel。
如果想要修改端口号、用户名和密码等信息,就在启动jar包时指定相关配置项(类似IDEA中通过VM options来启动多个服务):
java -jar -Dserver.port=8100 -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=admin -Dserver.servlet.session.timeout=1440m sentinel-dashboard-1.8.8.jar
4 限流
4.1 Java实现Sentinel的限流
4.1.1 引入依赖
<dependencies><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId><version>1.8.6</version></dependency></dependencies>
4.1.2 定义资源
资源就是要限流或熔断的目标,可以是代码块,也可以是接口,甚至服务,只要是Java中的内容都可以成为资源。SphU.entry()用来定义资源:
//资源:要限流的代码块或接口private static void test(int i) {try (Entry entry = SphU.entry("resource1")) {//业务逻辑处理System.out.println("执行test方法" + i);} catch (BlockException e) {//触发限流时的逻辑System.out.println("触发限流.." + i);}}
4.1.3 定义限流规则
限流规则定义了针对哪个资源,使用哪种等级的限流策略,什么情况下触发限流。
FlowRule用来定义规则对象,setGrade用来设置限流级别:RuleConstant.FLOW_GRADE_QPS(根据设置的QPS(每秒请求数)来限流)、RuleConstant.FLOW_GRADE_THREAD(根据并发线程数来限流);setCount用来设置限流阈值;通过FlowRuleManager.loadRules来载入限流规则。
//限流规则private static void initFlowRules() {List<FlowRule> rules = new ArrayList<>();FlowRule rule = new FlowRule();rule.setResource("resource1");rule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流等级是每秒的QPSrule.setCount(10);//超过10次限流rules.add(rule);FlowRuleManager.loadRules(rules);}
4.1.4 调用资源
public static void main(String[] args) {initFlowRules();for (int i = 0; i < 20; i++) {test(i);}}
定义20次请求,前10次没有超过限流阈值,不进行限流。后10次超过限流阈值,进行限流。
4.2 Spring Cloud集成Sentinel实现限流
4.2.1 引入依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
spring-cloud-starter-alibaba-sentinel依赖是对sentinel-core的封装,让其能与SpringCloud框架更好地兼容。
4.2.2 添加配置项
spring:cloud:sentinel:transport:dashboard: 127.0.0.1:8100 #sentinel控制台地址client-ip: 127.0.0.1 #连接本地Sentinel可以不配置,连接服务器Sentinel需要配置client-ip
spring-cloud-starter-alibaba-sentinel依赖自动把所有的Spring MVC的HTTP服务(即接口)提供流量埋点,即把所有的接口都自动定义为资源,因此不需要写定义资源的代码即可实现限流。如果想对非接口的方法实现限流,就需要类似上述Java实现Sentinel的限流的方式,定义资源和限流规则来实现。
4.2.3 Sentinel监控
启动项目后,访问接口,在Sentinel控制台可以实时监控流量。
簇点链路表示请求进入服务后的调用链路,即先经过DispatcherServlet,然后经过Controller、Service、Mapper层,请求处理完成后再层层返回响应,这样的调用链路就是簇点链路。
4.3 Sentinel流量控制
在Sentinel的控制台的簇点链路页面中,根据想要限流的接口选择流控选项可以进行流量控制:
这里选择阈值为1,手动请求接口,观察现象:
当快速发送请求(1秒内超过1次),就会出现限流的页面。
4.3.1 基于QPS/并发线程数的流量控制
由于如果想看更高的QPS,手动难以实现,因此使用jmeter(测试工具)来写测试计划,批量发送请求:
这里的线程数可以简单理解为有100个请求,在5秒内发送完毕,每分钟平均20个请求,即QPS为20。
注意:由于测试计划启动时间不一定是每分钟的开始,因此QPS显示不一定为20,但是可以认为近似等于20。
并发线程数是指同时占用的线程数量,为了避免线程数快速耗尽导致无法处理其它请求,就需要对并发线程数进行限流。
4.3.2 流控效果
流控效果分为快速失败、Warm Up、排队等待三种。
快速失败:对应RuleConstant.CONTROL_BEHAVIOR_DEFAULT,当请求的QPS达到阈值时,超过阈值的请求就会全部失败,抛出FlowException异常,即页面显示的Blocked by Sentinel (flow limiting)。适用于已经对系统压测过知道QPS上限的情况。
Warm Up:对应RuleConstant.CONTROL_BEHAVIOR_WARM_UP,预热模式,在设置的预热时间内,请求的QPS阈值初始值为阈值(maxThreshold)/冷因子(coldFactor),coldFactor默认为3,然后从初始阈值逐渐上升到设置的阈值。适用于冷启动情况,比如系统刚启动成功(系统内许多资源还未初始化或从未使用过,接口响应时间可能较慢),或者长期流量较低然后流量突然提高很多,冷启动情况下,请求很多时系统可能会被压垮。
排队等待:对应RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER,设置阈值的QPS后,请求会被严格分布在每秒的固定时间间隔,比如阈值为10,则每100ms通过一个请求。通过设置超时时间,可以让等待超时的请求直接失败。假设阈值为1,超时时间为5s,某一时刻同时发送10个请求,则每隔一秒通过一个请求,前6个请求等待时间小于5s,最后4个请求等待时间超过5s,则这4个请求会被直接丢失。适用于每隔一段时间流量高峰,其余时间较空闲的情况(排队等待就起到“削峰填谷”的作用)。
注意:区分快速失败和排队等待。快速失败是某一秒QPS超过阈值,超过部分会全部失败。而排队等待即使超过阈值,只要没有超过超时时间,就会等待通过。
下面演示Warm Up的情况,设置阈值10,设置预热时间为5s,而测试10s共100个请求(QPS约等于10):
可以看到,初始阈值为3(10(设置的阈值)/3(冷因子)),因此QPS为10的请求频率大部分被拒绝;然后5秒内逐渐上升到阈值为10,此时由于阈值=QPS,因此请求全部通过。
下面演示排队等待的情况,设置阈值为1,超时时间为5s,同时发送10个请求(1s内,近似等于同时发送),观察现象:
可以看到在第2秒(20:39:54)时,由于已经计算出有4个请求会超时,因此直接拒绝了这4个请求,其余6个由于阈值是1,因此每1s通过1个。
4.3.3 流控模式
流控模式分为直接、关联和链路,主要是对资源的调用链的流量来进行管理的。
直接:针对资源的任意调用来源(入口),即default(表示所有来源),都进行限流,只要所有来源的QPS总和超过阈值,就会限流。
链路:针对指定链路访问本资源进行限流,即资源的调用入口,只要该入口QPS超过阈值就限流。
关联:针对具有关联关系(两个资源共同竞争其它资源,这两个资源就是关联)的资源,当一个资源QPS超过阈值,另一个资源就要被限流。比如数据库的读操作通常要为写操作让道(写频繁的情况下),当写操作QPS超过阈值,读操作就限流。
下面演示链路流控模式,构建资源queryOrder,有两个调用该资源的接口:read()和write(),针对read()进行限流:
@SentinelResource("queryOrder")是定义资源的注解,括号内填入资源的名称:
@SentinelResource("queryOrder")public void queryOrder(){System.out.println("查询订单信息");}
由于Sentinel默认对所有SpringMVC的请求设置同一个监控资源sentinel_spring_web_context,因此如果要监控两条链路,就会导致一条链路看不到:
这需要关闭Sentinel的链路整合功能:
spring:cloud:sentinel:web-context-unify: false #关闭context整合
针对queryOrder资源设置限流规则,启用链路模式,只对read接口限流,同时发起两个接口的测试任务:
结果发现,read接口进行了限流,而write接口未进行限流。
下面演示关联流控模式,基于上述的两个接口read和write,希望read在write频繁时,read进行限流:
注意:哪个接口要限流,就需要对哪个接口进行规则的设置。关联资源是触发阈值的判定条件。
可以发现,虽然阈值为5,但是write并没有受到阈值的影响,这就是因为read进行限流,为write让道。
4.3.4 热点参数限流
热点参数限流是指当接口的URL携带参数时,可以针对特殊的参数和参数值进行限流。就比如某个数据经常被访问,称为热点数据,热点数据应该把限流QPS设置的更高些,而其它冷门数据的限流QPS设置的更低些,从而充分利用资源。
要对热点参数限流,URL带参数的接口就不能用默认的资源来设置限流,而是需要再自定义资源:
@SentinelResource("order-param")@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {return orderService.selectOrderById(orderId);}
在簇点链路中选中资源点击热点:
编辑热点规则,参数索引是指参数位于URL的参数部分的第几个参数(索引从0开始);单机阈值是针对该参数的所有值都统计限流;统计窗口时长是指在该时间内出现相同参数值的请求的QPS是否超过单机阈值;
参数例外项需要高级选项,首次编辑热点规则并没有,需要首次编辑后再点击热点选项才会出现。参数例外项是针对特殊参数值的接口进行自定义的限流规则,定义了例外项的会按限流阈值统计,没有定义的就默认按单机阈值统计。
向三个特殊参数值order/1、order/2、order/3的接口分别发送QPS为10的请求,共3s:
由于order/1定义的热点参数阈值(限流阈值)为7,QPS为10超过7,因此每7个请求会通过,每秒内剩下的请求会失败。order/2定义的热点参数阈值(限流阈值)为12,QPS没有超过,因此所有请求都会通过。而order/2没有定义限流阈值,因此按单机阈值为5限流,于是每5个请求通过,剩下的就会失败。
下篇文章: