服务熔断和限流
为什么需要流控降级
一般情况下会出现这几种情况导致服务不稳定:
- 瞬时洪峰流量大于系统的最大负载,导致系统崩溃
- 某些商品或服务出现缓存击穿,数据库出现问题,导致正常流量被挤占
- 调用方被某些不稳定的服务拖垮,线程跑满,线程池占满,导致整个系统的调度链路被卡死
因此我们需要进行一些处理,来解决这些时段出现的"流量洪峰"问题,使得每次的请求都变得平滑,尽可能在能处理掉这些请求的同时保证我们的服务不会被打垮.
因此我们主要采用流量控制和熔断降级的方式来保障微服务的稳定性
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
熔断降级是降低调用链路中的不稳定资源
在现代的微服务架构基本都是分布式的,由很多的服务组成.而且不同的服务之间还会有相互调用的情况,组成的链路极度复杂,那么在这么复杂庞大的服务体系中,如果某一环不稳定,就有可能出现层层级联的情况,最终导致整个链路都不可以使用,就像滚雪球一样,也就是出现 服务雪崩 ,所以我们需要对不稳定的弱依赖服务进行熔断降级,就像家里用的保险丝一样,暂时切断这个不稳定的调用,以避免局部的不稳定引发系统的雪崩
Spring Cloud Hystrix
SpringCloudHystrix实现了断路器、线程隔离等一系列服务保护功能, 是基于Netflix的开源框架Hystrix实现
通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供容错能力
快速使用
引入hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
在启动类中
package com.xjk.eurekaconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author XJK
*/
@EnableCircuitBreaker // 开启断路器功能
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaconsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaconsumerApplication.class, args);
}
}
服务层中
package com.xjk;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.stereotype.Service;
/**
* @author: XJK
*/
@Service
public class MyService {
@HystrixCommand(fallbackMethod = "error") // 指定出现的问题的时候该调用的回调方法
public String helloService(){
return "ook";
}
public String error(){
return "no ok";
}
}
hystrix关于熔断器的详细执行逻辑:
但由于hystrix是属于netflix那一套体系,而netflix已经慢慢地不能满足于我们当下的服务治理的需要的
随着Spring Cloud 2020.0.0版本的到来, 正式宣布了全面抛弃Netflix技术栈,这也就意味着hystrix已经进入了维护模式
取而代之的是Resilience4J和Spring Retry或者使用Alibaba的一站式解决方案,与Hystrix对应的是Sentinel
Spring Cloud Alibaba Sentinel
Sentinel是阿里巴巴开源的,面向分布式服务架构的高可用防护组件,主要以流量为切入点,从流量控制、流量整形、熔断降级、系统自适应保护、热点防护等多个维度来帮助 开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、自适应流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供Istio/Envoy 全局流控支持来为 Service Mesh 提供高可用防护的能力。
Sentinel特点
特点:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 是如何工作的
Sentinel 的主要工作机制如下:
- 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
- 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
- Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
Sentinel与Hystrix
在原来的 Spring Cloud Netflix 系列中,有自带的熔断组件 Hystrix ,是 Netflix公司提供的一个开源的组件,提供了熔断、隔离的这些特性,不过 Hystrix 在 2018 年 11 月份开始,就不再迭代开发,进入维护的模式。同年开源的 Spring Cloud Alibaba(SCA) 提供了一站式的解决方案,默认为 Sentinel 整合了 Spring Web、RestTemplate、FeignClient 和 Spring WebFlux。Sentinel 在 Spring Cloud 生态中,不仅补全了 Hystrix 在 Servlet、RestTemplate 和 API Gateway 这一块的空白,而且还完全兼容了 Hystrix 在 FeignClient 中限流降级的用法,并且支持运行时灵活地配置和调整限流降级规则。同时 SCA 还集成了 Sentinel 提供的 API gateway 流控模块,可 以无缝支持 Spring Cl oud Gateway 和 Zuul 网关的 流控降级。
Sentinel控制台环境下载与搭建
Sentinel 控制台jar包下载
启动命令:
java -jar -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard sentinel-dashboard.jar
其中
启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port
指定控制台地址和端口。
若启动多个应用,则需要通过 -Dcsp.sentinel.api.port=xxxx
指定客户端监控 API 的端口(默认是 8719)。
-Dproject.name=sentinel-dashboard
的意义是和spring.application.name一样的
这样就可以通过浏览器访问控制台
用户名密码默认都是sentinel
至此,Sentinel控制台环境安装就成功了
具体详细的配置项请参考官网:https://sentinelguard.io/zh-cn/docs/startup-configuration.html
Nacos服务接入Sentinel
启动nacos服务器
startup.cmd -m standalone
端口8848
启动Sentinel
端口8080
创建一个新的模块 sentinel-9003
引入pom依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
配置properties或yaml
# 应用名称
spring.application.name=sentinel-9003
# 应用服务 WEB 访问端口
server.port=9003
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 配置Sentinel dashboard地址
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719
spring:
application:
name: sentinel-9003
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
port: 8719
server:
port: 9003
启动类上添加@EnableDiscoveryClient
这是使Nacos Server能够发现当前服务,用来注册
package com.xujiakai.sentinel9003;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class Sentinel9003Application {
public static void main(String[] args) {
SpringApplication.run(Sentinel9003Application.class, args);
}
}
编写Controller
package com.xujiakai.sentinel9003.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author: XJK
*/
@Controller
@RequestMapping("/s")
public class MyController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
@ResponseBody
public String hello(@RequestParam("n")String name){
return "hello,"+name;
}
@RequestMapping(value = "/hi",method = RequestMethod.GET)
@ResponseBody
public String hello2(){
return "hi~~~~~~~~~~";
}
}
测试访问
- 启动Sentinel9003Application
- 浏览器访问Controller,并短时间多次访问
-
进入Sentinel控制台查看当前服务
实时监控
从实时监控中可以看到,我们频繁快速地访问/s/hello和 /s/hi的时候,这个监控界面就会出现波动,这说明sentinel正在监控着当前这个服务
Sentinel流量控制
点开高级选项
之前我们大致了解了什么是流量控制
现在我们需要对上面图中的名词进行解释
名词解释
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量 Queries-per-second):当调用该API的QPS达到阈值的时候,进行限流
- 线程数:当调用该API的线程数量达到阈值的时候,进行限流
- 是否集群:当前不需要集群
- 流控模式:
- 直接:API达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)(API级别的针对来源)
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFacotor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
通过控制台添加流控有两种方式:
- 直接选择通过流控规则进行流量控制
- 通过簇点链路进行指定的流控
我们这里采用第二种方式测试流控
我们这里给/s/hi进行流控设置
流控模式分为:
- 直接
- 关联
- 链路
流量模式 - 直接模式
流量控制主要有两种阈值统计类型,一种是统计线程数,另外一种则是统计 QPS。
QPS 快速失败
阈值类型选择QPS, 单机阈值设置为1
这意味着1秒中只能请求量只有一次,次数过多就不让访问了,也就是说,超过了这个阈值,就被Sentinel阻塞了,这里前端页面直接显示的原因是流控效果选择的是快速失败,也就会直接显示错误
QPS流量控制
当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的手段包括下面 3 种,对应 FlowRule
中的 controlBehavior
字段:
- 直接拒绝(
RuleConstant.CONTROL_BEHAVIOR_DEFAULT
)方式。该方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。 - 冷启动(
RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。 - 匀速器(
RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式。这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
并发线程数 快速失败
阈值类型选择并发线程数其他不变
那么为了体现出业务线程,我们先在Controller /s/hi的实现方法中加入 sleep 2秒来模拟正在执行业务方法
@RequestMapping(value = "/hi",method = RequestMethod.GET)
@ResponseBody
public String hello2(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hi~~~~~~~~~~";
}
重启服务,重新访问
此时访问sentinel中的簇点链路是空的
由于Sentinel与当前流经的流量有关,所以需要重新访问 /s/hi之后再去设置流控
此时在第一个浏览器访问 /s/hi 还没加载出来的时候,第二个浏览器也访问 /s/hi
那么在另外的访问线程则会出现错误
并发线程数流量控制
线程数限流用于保护业务线程数不被耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对高线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离),或者使用信号量来控制同时请求的个数(信号量隔离)。这种隔离方案虽然能够控制线程数量,但无法控制请求排队时间。当请求过多时排队也是无益的,直接拒绝能够迅速降低系统压力。Sentinel线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程个数,如果超出阈值,新的请求会被立即拒绝。
流量模式 - 关联模式
调用关系包括调用方、被调用方;方法又可能会调用其它方法,形成一个调用链路的层次关系。Sentinel 通过 NodeSelectorSlot
建立不同资源间的调用的关系,并且通过 ClusterNodeBuilderSlot
记录每个资源的实时统计信息。
有了调用链路的统计信息,我们可以衍生出多种流量控制手段。
具有关系的资源流量控制:关联流量控制
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db
和 write_db
这两个资源分别代表数据库读写,我们可以给 read_db
设置限流规则来达到写优先的目的:设置 FlowRule.strategy
为 RuleConstant.RELATE
同时设置 FlowRule.ref_identity
为 write_db
。这样当写库操作过于频繁时,读数据的请求会被限流。
关联流控测试使用
修改controller
@RequestMapping(value = "/h1",method = RequestMethod.GET)
@ResponseBody
public String h1(){
return "hi~~~~~~~~~~first";
}
@RequestMapping(value = "/h2",method = RequestMethod.GET)
@ResponseBody
public String h2(){
return "hi~~~~~~~~~~second";
}
给/s/h1设置关联流控规则
为了达到足够的并发量进行测试,我们需要借用其他工具(我这里使用的是ApiFox)进行测试
在apifox运行的过程中访问/s/h1
这说明,在/s/h2请求量极大的时候,会阻塞/s/h1的访问,一般情况下,/s/h2的前一个模块是/s/h1,减少了/s/h1的流量,进而减少/s/h2的流量
流量模式 - 链路模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流,它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。
根据调用链路入口限流:链路限流
NodeSelectorSlot
中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root
的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:
machine-root
/ \
/ \
Entrance1 Entrance2
/ \
/ \
DefaultNode(nodeA) DefaultNode(nodeA)
上图中来自入口 Entrance1
和 Entrance2
的请求都调用到了资源 NodeA
,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 FlowRule.strategy
为 RuleConstant.CHAIN
,同时设置 FlowRule.ref_identity
为 Entrance1
来表示只有从入口 Entrance1
的调用才会记录到 NodeA
的限流统计当中,而对来自 Entrance2
的调用漠不关心。
调用链的入口是通过 API 方法 ContextUtil.enter(name)
定义的。
测试使用
首先编写一个pojo
package com.xujiakai.sentinel9003.pojo;
/**
* @author: XJK
*/
public class Res {
private String name;
public Res() {
}
public Res(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接着我们编写一个Service
package com.xujiakai.sentinel9003.service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.xujiakai.sentinel9003.pojo.Res;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author: XJK
*/
@Service
public class MyService {
// 定义限流资源
@SentinelResource("common")
public List<Res> common(){
ArrayList<Res> list = new ArrayList<>();
list.add(new Res("AAAAA"));
list.add(new Res("BBBBB"));
list.add(new Res("CCCCC"));
list.add(new Res("DDDDD"));
return list;
}
}
然后更改接口调用这个Service方法
@RestController
public class MyController {
@Autowired
MyService myService;
@GetMapping("/testA")
public String testA(){
return myService.common();
}
@GetMapping("/testB")
public String testB(){
return myService.common();
}
}
还要在properties添加
spring.cloud.sentinel.web-context-unify=false
接下来配置流控规则:
这里要注意不要对/testA或者/testB进行限流规则的配置,要给用**@SentinelResource注解标注的资源**进行配置限流规则,这里的意思为当我们用入口资源访问被SentinelResource注解标注的资源方法时,当超过阈值就会被限流。
测试
访问同样的资源,testA没有限流,但是testB限流了
流控效果 - WarmUp
官网手册地址:https://sentinelguard.io/zh-cn/docs/flow-control.html
概念:Warm Up方式,即预热/冷启动方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。
预热公式:阈值/coldFactor(默认值为3),经过预热时间后才会达到阈值。
在到达巅峰前的过多QPS访问会返回error
过多的QPS会被拒绝访问
流控效果 - 排队等待
官方文档:https://sentinelguard.io/zh-cn/docs/flow-control.html
概念:匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求(削峰填谷)。
例图:
匀速器
它的中心思想是,以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过。否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。
Sentinel 匀速排队等待策略是漏桶算法结合虚拟队列等待机制实现的。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
一次处理一个,均匀处理
Sentinel熔断控制
基本介绍
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
熔断策略
Sentinel 提供了一下几种熔断策略:
-
慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 -
异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 -
异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
Sentinel在1.8.0版本对熔断降级做了大的调整,可以定义任意时长的熔断时间,引入了半开启恢复支持。下面梳理下相关特性。
熔断状态有三种状态,非别为OPEN、HALF_OPEN、CLOSED
状态 | 说明 |
---|---|
OPEN | 表示熔断开启,拒绝所有请求 |
HALF_OPEN | 探测恢复状态,如果接下来的一个请求顺利通过则表示结束熔断,否则继续熔断 |
CLOSE | 表示熔断关闭,请求顺利通过 |
熔断规则
熔断降级规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
官方文档网址:https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
熔断规则 慢调用比例
概念:选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
熔断规则 异常比例
概念:异常比例 (ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException
)不生效。
熔断规则 异常数
概念:异常数 (ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException
)不生效。
Sentinel热点控制
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
官网:https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html
这里还有相对应的高级选项,我们这里先了解基本规则。
使用@SentinelResource注解
其实这个热点限流其实就是更加细粒度的流控规则,那么如果想使用它就必须要配合对应SentinelResource注解。
Sentinel 提供了 @SentinelResource 注解用于定义资源,它有很多的参数,我们这里主要关注两个参数:
- value:代表资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的
- blockHandler:blockHandler 对应处理 BlockException 的方法名称,可选项,访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。
案例讲解
@SentinelResource(value=“xxx”)
那现在我们要完成以上图中的效果,这个时候我们首先要编写代码,在FlowLimitController中编写代码
@GetMapping("/testHotKey")
@SentinelResource("testHotKey")
public String testHotKey(@RequestParam(value = "hot1",required = false) String hot1,
@RequestParam(value = "hot2",required = false)String hot2,
@RequestParam(value = "hot13",required = false) String hot3){
return "----testHotKey";
}
然后再来配置热点规则
这里要说明一下,参数索引0实际上代表的就是我们设置的hot1参数
测试,此时如果我们传入参数hot1,并且超过阈值,就会出现限流,但是此时的限流效果为报错,显示不友好
@SentinelResource(value=“xxx”,blockHandler=“xxx”)
刚才的演示中,我们明显发现这种限流方法的提示效果非常不友好,所以如果我们需要能够得到友好的提示,我们就需要使用@SentinelResource注解提供的另外一个参数blockHandler,这个参数是可以指定当出现异常时的处理方法
参数例外项
其实参数例外项就是可以达到更加细粒度的控制,比如我们当前的例子中,目前hot1参数在访问时超过阈值就会被限流,但是我们可以通过参数例外项设置hot1具体等于特殊的某个值的时候,触发不同的限流效果。假如hot1的值等于5时,它的阈值可以达到200。
**注意:**参数例外项中的参数类型仅支持一下7种数据类型