任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制,保护系统不会被瞬间的流量冲垮,也可以预防恶意请求。
限流有以下几个角度:
-
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
-
运行指标,例如 QPS、线程池、系统负载等;
-
控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
Pt1 限流规则
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
FlowSlot
会根据预设的规则,结合前面 NodeSelectorSlot
、ClusterBuilderSlot
、StatisticSlot
统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,您可以捕捉 BlockException
来自定义被限流之后的处理逻辑。
同一个资源可以创建多条限流规则。FlowSlot
会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
-
resource
:资源名,即限流规则的作用对象 -
count
: 限流阈值 -
grade
: 限流阈值类型(QPS 或并发线程数) -
limitApp
: 流控针对的调用来源,若为default
则不区分调用来源 -
strategy
: 调用关系限流策略 -
controlBehavior
: 流量控制效果(直接拒绝、Warm Up、匀速排队)
Pt2 基于QPS/并发线程数
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS。类型由 FlowRule
的 grade
字段来定义。其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制。其中线程数、QPS 值,都是由 StatisticSlot
实时统计获取的。
我们可以通过下面的命令查看实时统计信息:
curl http://localhost:8719/cnode?id=resourceName
输出内容格式如下:
idx id thread pass blocked success total Rt 1m-pass 1m-block 1m-all exception
2 abc647 0 46 0 46 46 1 2763 0 2763 0
其中:
-
thread: 代表当前处理该资源的并发数;
-
pass: 代表一秒内到来到的请求;
-
blocked: 代表一秒内被流量控制的请求数量;
-
success: 代表一秒内成功处理完的请求;
-
total: 代表到一秒内到来的请求以及被阻止的请求总和;
-
RT: 代表一秒内该资源的平均响应时间;
-
1m-pass: 则是一分钟内到来的请求;
-
1m-block: 则是一分钟内被阻止的请求;
-
1m-all: 则是一分钟内到来的请求和被阻止的请求的总和;
-
exception: 则是一秒内业务本身异常的总和。
Pt2.1 并发线程数控制
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。
Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被直接拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
基于并发线程数限流示例说明:
<!-- 添加SpringBoot项目对Sentinel依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
// 1、项目启动加载Sentinel规则
@SpringBootApplication
public class MicroserviceApplication {
public static void main(String[] args) {
// 启动时将规则加载到内存
initFlowRule();
SpringApplication.run(MicroserviceApplication.class, args);
}
// 初始化限流规则
public static void initFlowRule() {
List<FlowRule> rules =