服务治理-10、限流熔断降级(服务保护)-Sentinel
1. 雪崩问题
1.1. 是什么
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。
1.2. 产生原因
微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。
如果服务提供者 I 发生了故障,当前的应用的部分业务因为依赖于服务 I,因此也会被阻塞。
但是,依赖服务 I 的业务请求被阻塞,用户不会得到响应,则 tomcat 的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞:
服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,那么当前服务也就不可用了。
1.3. 雪崩问题解决方案
❕ ^wihp2i
解决雪崩问题的常见方式有四种:
超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
**舱壁模式 (线程隔离)**:限定每个业务能使用的线程数,避免耗尽整个 tomcat 的资源,因此也叫线程隔离
熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求
流量控制:限制业务访问的 QPS,避免服务因流量的突增而故障
1.3.1. 超时处理
设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
1.3.2. 舱壁模式 (线程隔离)
限定每个业务能使用的线程数,避免耗尽整个 tomcat 的资源,因此也叫线程隔离。 ❕ ^pshp1j
1.3.3. 熔断降级
由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求
1.3.4. 流量控制
限制业务访问的 QPS,避免服务因流量的突增而故障
1.3.5. 总结
2. 微服务保护技术
3. 限流
3.1. 流控模式
在添加限流规则时,点击高级选项,可以选择三种 流控模式:
- 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
- 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
- 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
3.1.1. 关联
3.1.2. 链路
对请求来源的限流控制
❕ ^3uq11z
3.2. 流控效果
3.3. 热点参数限流
需要使用注解 @SentinelResource
,给热点参数限流方法再加一个资源 id 名称,比如下面的 “hot” ❕ ^wj1oql
3.4. 流量整形
排队等待的流控效果,可以起到流量整形的效果
4. 隔离和降级
https://www.bilibili.com/video/BV1LQ4y127n4?p=145&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
4.1. 线程隔离 (舱壁模式)
Sentinel 默认采用信号量隔离
4.2. 熔断降级
4.2.1. 断路器
4.2.2. 熔断策略
4.2.2.1. 慢调用
4.2.2.2. 异常比例、异常数
4.2.3. Feign 整合 Sentinel
Feign 开启 Sentinel 功能后,Sentinel 就会把 Feign 的调用视作一种资源,从而能够进行熔断降级操作。 ❕ ^gdjmu1
4.3. 授权规则
4.3.1. 作用
对请求者身份来源的校验,防止绕过网关直接调用微服务资源。只允许网关过来的请求 ❕ ^krg0d5
流控应用后面输入框中调用方名称填写请求来源,比如我们在请求头中增加一个表示 gateway 来源的请求头,例子中我们直接简单的使用字符串 “gateway”
4.3.2. 使用方法
4.3.2.1. 在 GateWay 中加请求头
加上此配置之后,凡是从网关路由到微服务的请求中都带有名称为 origin,值为 “gateway” 的请求头,而从浏览器中直接发送的请求 (企图绕过网关) 中没有约定的请求头,就会被拦截过滤掉。
4.3.2.2. Sentinel 解析请求头
Sentinel 是通过 RequestOriginParser 这个接口的 parseOrigin 来获取请求的来源的。
所以,需要编写一个实现类,实现获取请求来源的接口 RequestOriginParser
的 parseOrigin
方法,让 Sentinel 获取到请求来源,从而让其能够做过滤判断。
4.3.2.3. 优化
这一块可以使用 starter 引入的方式,就不需要在每个微服务中都编写同样的逻辑了
❕ ^ez1ivw
4.3.3. 自定义异常
5. Sentinel 原理
❕ ^htxflw
[[Sentinel源码分析.md]]
https://www.bilibili.com/video/BV1LQ4y127n4?p=176&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
Sentinel 实现限流、隔离、降级、熔断等功能,本质要做的就是两件事情:
- 统计数据:统计某个资源的访问数据(QPS、RT 等信息)
- 规则判断:判断限流规则、隔离规则、降级规则、熔断规则是否满足
这里的 资源 就是希望被 Sentinel 保护的业务,例如项目中定义的 controller 方法就是默认被 Sentinel 保护的资源、@SentinelResource 注解标注的自定义资源。
5.1. ProcessorSlotChain
❕ ^fazbix
实现上述功能的核心骨架是一个叫做 ProcessorSlotChain
的类。这个类基于责任链模式来设计,将不同的功能(限流、降级、系统保护)封装为一个个的 Slot,请求进入后逐个执行即可。
❕ ^p5myd9
5.1.1. 责任链中的 Slot 分为两大类
5.1.1.1. 数据构建及统计部分(statistic)
- NodeSelectorSlot:负责构建簇点链路中的节点(DefaultNode、EntranceNode,是一种特殊的 DefaultNode),将这些节点形成链路树
- ClusterBuilderSlot:负责构建某个资源的 ClusterNode,ClusterNode 可以保存资源的运行信息(响应时间、QPS、block 数目、线程数、异常数等)以及来源信息(origin 名称)
- StatisticSlot:负责统计实时调用数据,包括运行信息、来源信息等
5.1.1.2. 规则判断部分(rule checking)
❕ ^u9i9a5
- AuthoritySlot:负责授权规则(来源控制)
- SystemSlot:负责系统保护规则
- ParamFlowSlot:负责热点参数限流规则
- FlowSlot:负责限流规则
- DegradeSlot:负责降级规则
5.1.2. 执行流程
❕ ^8j0qwe
5.1.2.1. 自动装配 -SentinelWebAutoConfiguration
其中的 spring.factories 声明需要就是自动装配的配置类,内容如下:
我们先看 SentinelWebAutoConfiguration
这个类:
5.1.2.2. 注册拦截器 -SentinelWebInterceptor
这个类实现了 WebMvcConfigurer,我们知道这个是 SpringMVC 自定义配置用到的类,可以配置 HandlerInterceptor:
可以看到这里配置了一个 SentinelWebInterceptor
的拦截器。SentinelWebInterceptor
的声明如下:
发现它继承了 AbstractSentinelInterceptor
这个类。
HandlerInterceptor
就是我们 SpringMVC 中的拦截器了。 拦截器会拦截一切进入 controller 的方法,执行 preHandle
前置拦截方法,而 Context 的初始化就是在这里完成的。
5.1.2.3. 创建 Context
1 |
|
5.1.2.3.1. Context 作用
我们发现簇点链路中除了 controller 方法、service 方法两个资源外,还多了一个默认的入口节点:sentinel_spring_web_context
,是一个 EntranceNode 类型的节点
这个节点是在初始化 Context 的时候由 Sentinel 帮我们创建的。
那么,什么是 Context 呢?
- Context 代表调用链路上下文,贯穿一次调用链路中的所有资源(
Entry
),基于 ThreadLocal。 - Context 维持着入口节点(
entranceNode
)、本次调用链路的 curNode(当前资源节点)、调用来源(origin
)等信息。 - 后续的 Slot 都可以通过 Context 拿到 DefaultNode 或者 ClusterNode,从而获取统计数据,完成规则判断
- Context 初始化的过程中,会创建 EntranceNode,contextName 就是 EntranceNode 的名称
5.1.2.4. 进入执行入口 -SphU.entry
首先,回到一切的入口,AbstractSentinelInterceptor
类的 preHandle
方法:
还有,SentinelResourceAspect
的环绕增强方法:
5.1.2.5. 创建 ProcessorSlotChain
5.1.3. 关键配置
5.1.3.1. 统一入口
默认为 true,统一入口为
但如果做链路限流时,需要禁用统一入口,分成不同的链路,那么就需要设置这个参数为 false。服务治理-10、限流熔断降级(服务保护)-Sentinel
5.2. Node
所有的节点都可以记录对资源的访问统计数据,所以都是 StatisticNode 的子类。
按照作用分为两类 Node:
- DefaultNode:代表链路树中的每一个资源,一个资源出现在不同链路中时,会创建不同的 DefaultNode 节点。而树的入口节点叫 EntranceNode,是一种特殊的 DefaultNode
- ClusterNode:代表资源,一个资源不管出现在多少链路中,只会有一个 ClusterNode。记录的是当前资源被访问的所有统计数据之和。
DefaultNode 记录的是资源在当前链路中的访问数据,用来实现基于链路模式的限流规则。ClusterNode 记录的是资源在所有链路中的访问数据,实现默认模式、关联模式的限流规则。
例如:我们在一个 SpringMVC 项目中,有两个业务:
- 业务 1:controller 中的资源
/order/query
访问了 service 中的资源/goods
- 业务 2:controller 中的资源
/order/save
访问了 service 中的资源/goods
创建的链路图如下:
5.3. Entry
默认情况下,Sentinel 只会将 controller 中的方法作为被保护资源,那么问题来了,我们该如何将自己的一段代码标记为一个 Sentinel 的资源呢?
5.3.1. 编码方式
5.3.2. 基于注解标记资源
5.3.2.1. 实现原理 -SentinelAutoConfiguration
❕ ^odpucd
来看下我们引入的 Sentinel 依赖包:
其中的 spring.factories 声明需要就是自动装配的配置类,内容如下:
我们来看下 SentinelAutoConfiguration
这个类:
可以看到,在这里声明了一个 Bean,SentinelResourceAspect
:
1 |
|
简单来说,@SentinelResource 注解就是一个标记,而 Sentinel 基于 AOP 思想,对被标记的方法做环绕增强,完成资源(Entry
)的创建。
6. 面试题
6.1. Sentinel 的线程隔离与 Hystix 的线程隔离有什么差别?⭐️🔴
线程池隔离:在业务请求到达 Tomcat 后,会给每一个被隔离的业务创建各自独立的线程池
信号量隔离:基于计数的信号量隔离
Hystix 默认是基于线程池实现的线程隔离,每一个被隔离的业务都要创建一个独立的线程池,线程过多会带来额外的 CPU 开销,性能一般,但是隔离性更强。
Sentinel 是基于信号量(计数器)实现的线程隔离,不用创建线程池,性能较好,但是隔离性一般。
6.2. Sentinel 的限流与 Gateway 的限流有什么差别?
❕ ^n7rdsh
限流算法常见的有三种实现:滑动时间窗口、令牌桶算法、漏桶算法。
Gateway 则采用了基于 Redis 实现的令牌桶算法。 ^cinpaa
而 Sentinel 内部却比较复杂:
- 默认限流模式 是基于滑动时间窗口算法
- 排队等待限流 模式则基于漏桶算法 (Leaky Bucket)
- 热点参数限流模式 则是基于令牌桶算法 (Token Bucket)
6.2.1. 固定窗口计数器算法
存在问题:前 1 秒的后半秒 3 个,后 1 秒的前半秒也有请求,就造成 1 秒内超过 3 个请求了,比如上图所示,前半秒 3 个后半秒 3 个达到了 6 个。
6.2.2. 滑动窗口计数器算法
存在问题:仍然会存在超阈值 (比如 3 个) 的情况,比如下图 850ms 内来了 4 个请求
6.2.3. 令牌桶算法
6.2.3.1. 存在问题
请求的时间分布不均匀时,可能出现短时间内达到阈值上限的情况。比如限流阈值为 5,设置令牌桶每秒生成 5 个令牌,前 1 秒没有任何请求过来,而第 2 秒一下子来了 10 个请求,那么第二秒总共生成的 10 个令牌就一下子被使用,而系统服务的上限是 5,那么就出现了过载现象。所以令牌桶的上限要设置为小于系统并发上线的数值,以防止出现上述情况。
6.2.3.2. 实现方式⭐️🔴
❕ ^06vdeq
实际实现并没有真正去生成什么令牌,而是基于计数和记录上次请求的时间
- 如果本次请求时间与上次请求时间差是 1 秒内的,如果此时桶里有令牌可用,那么令牌已使用计数加 1,并且消耗 1 个令牌。如果没有令牌了则拒绝请求
- 如果本次请求时间与上次请求时间差大于 1 秒,就比较已使用令牌数与时间差值可以生成的令牌数的大小,如果时间差值生成令牌数大于已使用令牌数,那么说明令牌还有富余,则接受请求,否则说明超出令牌生成能力范围,则拒绝请求。
6.2.4. 漏桶算法
相比于令牌桶,漏桶的请求处理曲线非常平滑,也可以应对流量突发请求流量
预期等待时长=最近一次请求的预期等待时间 + 允许的间隔
6.2.5. 限流算法对比
❕ ^6pp17h
7. 实战经验
8. 参考与感谢
8.1. 黑马程序员
8.1.1. 视频
https://www.bilibili.com/video/BV1LQ4y127n4?p=144&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
8.1.2. 资料
1 |
|