文章目录
前言
在上篇中,主要记录了Sentinel与Spring Boot的整合,以及责任链的构建,执行的过程。在责任链中,与规则校验,熔断降级有关的主要是最后四个节点:AuthoritySlot、SystemSlot、FlowSlot、DegradeSlot
。在校验不通过,或者触发熔断降级时,通常会抛出异常:
AuthoritySlot
抛出的是AuthorityException
:
SystemSlot
抛出的是SystemBlockException
:
FlowSlot
抛出的是FlowException
:
DegradeSlot
抛出的是DegradeException
:
这四个Sentinel自定义的异常,都有一个共同点,是BlockException
的子类。抛出的异常,又会被责任链中的StatisticSlot
节点捕获,并执行一系列的逻辑,然后继续向上抛出。
最后会被最外层的SentinelResourceAspect
切面捕获:
一、StatisticSlot的异常处理
在责任链的StatisticSlot
节点中,执行完后续责任链的代码,实际上还会进行一些资源的处理,例如增加当前线程调用数,后续规则校验通过的请求数:
在捕捉到了后续四个责任链节点抛出的BlockException
后,StatisticSlot
会统计被规则限流的调用数量,然后继续向上抛出异常。
二、SentinelResourceAspect的异常处理
SentinelResourceAspect
的异常处理,分为两部分,即处理BlockException
和Throwable
:
BlockException
是不符合规则,或者熔断降级之后抛出的异常,会被@SentinelResource
注解中的blockHandler
所标注的方法进行处理:
通过反射调用
Throwable
的异常,是用户的业务代码抛出的异常,由@SentinelResource
注解中的blockHandler
所标注的方法进行处理:
通过反射调用
三、entry.exit
在SentinelResourceAspect
切面中,无论是否出现异常,entry.exit
是一定会执行的方法。该方法会逐个调用责任链节点的exit方法:
每个责任链节点都有exit方法
NodeSelectorSlot
、ClusterBuilderSlot
、AuthoritySlot
、SystemSlot
、FlowSlot
的exit方法都是直接调用下一个节点,LogSlot
则是记录日志,重点是StatisticSlot
和DegradeSlot
。
2.1、StatisticSlot的exit
在StatisticSlot
的exit中,首先会获取链路中的当前节点:
如果 BlockError 为 null,说明这次请求是通过了限流校验的,才进行后续统计,并且会用当前时间 - 创建时间
,统计出该次请求的调用时间:
然后会获取请求中的其他错误信息,并且记录完成统计(响应时间、成功数、异常数):recordCompleteFor方法
recordCompleteFor方法:
- 记录响应时间,以及调用完成数
- 释放线程数
2.2、DegradeSlot的exit
DegradeSlot
的exit方法,会获取链路中的当前资源访问记录:
如果之前已经被拦截(限流/熔断等),直接调用下一个 Slot 的 exit()。**这种情况说明该请求已经被熔断降级了,熔断器处于open状态,不需要更新状态。**没有熔断规则,说明这个资源不需要熔断逻辑,也会直接调用下一个 Slot 的 exit():
能走到这一步,说明本次请求是正常通过的,可能存在两种情况:
- 本身熔断器没有触发熔断,处于关闭状态。
- 熔断器熔断时间已过,本次请求是熔断时长结束后的下一个请求。
有两个不同的实现,分别是ExceptionCircuitBreaker
和 ResponseTimeCircuitBreaker
前者对应调用异常数比例,后者对应慢调用比例。
在ResponseTimeCircuitBreaker
的onRequestComplete
方法中,如果请求处理时间大于阈值,则慢调用数量 + 1,无论是否超过阈值,总调用次数都会 + 1。
然后会调用handleStateChangeWhenThresholdExceeded
处理熔断器状态,如果熔断器已经处于打开
的状态,就无需处理,直接结束。
然后是处理半开
状态:
- 本次调用的时间,大于阈值,则熔断器重新开启。更新熔断时间
- 本次调用的时间,小于等于阈值,则熔断器关闭。重置慢调用和总调用次数为0
无论是开启还是关闭,都是CAS操作,并且会通知观察者执行自己的逻辑(也就是用户可以自定义熔断器状态变化时执行的逻辑)
最后的这段逻辑,是处理熔断器关闭的情况。如果慢调用的比例超过了阈值,则会打开熔断器。
ExceptionCircuitBreaker
是同样的道理,只不过统计的维度成为了异常比例。
总结
在 Sentinel 的异常体系中,主要分为两类:
- 第一类是继承了
BlockException
的异常,通常由限流、熔断降级等规则触发,表示“资源访问被拒绝”; - 第二类是用户程序在执行过程中产生的运行时异常,例如空指针、业务异常等。
Sentinel 的异常处理主要体现在 StatisticSlot
和 SentinelResourceAspect
中。前者用于统计异常信息,并将异常抛出;后者则负责实际处理这些异常,通常通过用户在注解中配置的回调方法处理:
- 对于继承了
BlockException
的异常,会被@SentinelResource
注解中的blockHandler
方法处理; - 对于用户代码中的异常(非
BlockException
),则由@SentinelResource
注解中的fallback
方法进行兜底处理。
上述处理逻辑最终都是通过反射机制调用用户定义的方法完成的。
此外,在 Sentinel 的责任链中,除了 entry
方法用于进入资源,还存在一个用于资源释放的 exit
方法,常见的 Slot 有两个关键实现:
StatisticSlot
的exit
方法用于统计调用完成数、响应时间、异常信息等,并释放当前线程数;DegradeSlot
的exit
方法则主要用于更新熔断器状态。Sentinel 提供了两类熔断策略:异常比率熔断 和 慢调用比率熔断:- 如果当前熔断器处于“打开(OPEN)”状态,则不会进行任何更新;
- 如果熔断器处于“半开(HALF_OPEN)”状态:
- 若本次调用异常,或响应时间超过阈值,熔断器重新转为打开状态,并更新熔断时间;
- 若调用正常,且响应时间未超过阈值,则熔断器进入关闭(CLOSED)状态,并重置统计数据;
- 若熔断器处于“关闭(CLOSED)”状态,若检测到异常比例或慢调用比例超过阈值,则触发熔断,状态转为“打开”。
在这一过程中,Sentinel 也采用了观察者模式,允许用户注册监听器,在熔断器状态发生变化时执行自定义逻辑(如报警、日志等)。
下一篇:Sentinel核心算法解析