限流是保障服务高可用的方式之一,尤其是在微服务架构中,对接口或资源进行限流可以有效地保障服务的可用性和稳定性。 之前的项目中使用的限流措施主要是Guava的RateLimiter。RateLimiter是基于令牌桶流控算法,使用非常简单,但是功能相对比较少。 而现在,我们有了一种新的选择,阿里提供的 Sentinel。 Sentinel 是阿里巴巴提供的一种限流、熔断中间件,与RateLimiter相比,Sentinel提供了丰富的限流、熔断功能。它支持控制台配置限流、熔断规则,支持集群限流,并可以将相应服务调用情况可视化。 目前已经有很多项目接入了Sentinel,而本文主要是对Sentinel的限流功能做一次详细的分析,至于Sentinel的其他能力,则不作深究。 一、总体流程 先来了解一下总体流程: 上面的图是官网的图, 从设计模式上来看,典型的的责任链模式。外部请求进来后,要经过责任链上各个节点的处理,而Sentinel的限流、熔断就是通过责任链上的这些节点实现的。 从限流算法来看,Sentinel使用滑动窗口算法来进行限流。要想深入了解原理,还是得从源码上入手,下面,直接进入Sentinel的源码阅读。 二、源码阅读 1. 源码阅读入口及总体流程 读源码先得找到源码入口。我们经常使用@ SentinelResource来标记一个方法,可以将这个被@ SentinelResource标记的方法看成是一个Sentinel资源。因此,我们以@ SentinelResource为入口,找到其切面,看看切面拦截后所做的工作,就可以明确Sentinel的工作原理了。直接看注解@SentinelResource的切面代码(SentinelResourceAspect)。 可以清晰的看到Sentinel的行为方式。进入SentinelResource切面后,会执行SphU.entry方法,在这个方法中会对被拦截方法做限流和熔断的逻辑处理。 如果触发熔断和限流,会抛出BlockException,我们可以指定blockHandler方法来处理BlockException。而对于业务上的异常,我们也可以配置fallback方法来处理被拦截方法调用产生的异常。 所以,Sentinel熔断限流的处理主要是在SphU.entry方法中,其主要处理逻辑见下图源码。 可见,在SphU.entry方法中,Sentinel实现限流、熔断等功能的流程可以总结如下: 获取Sentinel上下文(Context); 获取资源对应的责任链; 生成资源调用凭证(Entry); 执行责任链中各个节点。 接下来,围绕这几个方面,对Sentinel的服务机制做一个系统的阐述。 2. 获取Sentinel上下文(Context) Context,顾名思义,就是Sentinel熔断限流执行的上下文,包含资源调用的节点和Entry信息。 来看看Context的特征: Context是线程持有的,利用ThreadLocal与当前线程绑定。 这里就引出了Sentinel的三个比较重要的概念:Conetxt,Node,Entry。这三个类是Sentinel的核心类,提供了资源调用路径、资源调用统计等信息。 Context Context是当前线程所持有的Sentinel上下文。 进入Sentinel的逻辑时,会首先获取当前线程的Context,如果没有则新建。当任务执行完毕后,会清除当前线程的context。Context 代表调用链路上下文,贯穿一次调用链路中的所有 Entry。 Context 维持着入口节点(entranceNode)、本次调用链路的 当前节点(curNode)、调用来源(origin)等信息。Context 名称即为调用链路入口名称。 Node Node是对一个@SentinelResource标记的资源的统计包装。 Context中记录本当前线程资源调用的入口节点。 我们可以通过入口节点的childList,可以追溯资源的调用情况。而每个节点都对应一个@SentinelResource标记的资源及其统计数据,例如:passQps,blockQps,rt等数据。 Entry Entry是Sentinel中用来表示是否通过限流的一个凭证,如果能正常返回,则说明你可以访问被Sentinel保护的后方服务,否则Sentinel会抛出一个BlockException。 另外,它保存了本次执行entry()方法的一些基本信息,包括资源的Context、Node、对应的责任链等信息,后续完成资源调用后,还需要更具获得的这个Entry去执行一些善后操作,包括退出Entry对应的责任链,完成节点的一些统计信息更新,清除当前线程的Context信息等。 3. 获取@SentinelResource标记资源对应的责任链 资源对应的责任链是限流逻辑具体执行的地方,采用的是典型的责任链模式。 先来看看默认的的责任链的组成: 默认的责任链中的处理节点包括NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot、FlowSlot、DegradeSlot等。调用链(ProcessorSlotChain)和其中包含的所有Slot都实现了ProcessorSlot接口,采用责任链的模式执行各个节点的流量交易处理逻辑,并调用下一个节点。 每个节点都有自己的作用,后面将会看到这些节点具体是干什么的。 |
|