的整个工作流程这篇文章:Sentinel是如何消耗性能的,通过复用Buckets来减少内存占用。在统计QPS时,会使用当前时间戳来定位Buckets。LongAdder用于统计在时间窗口内请求成功的次数,失败的次数,以及花费的总时间来优化并发锁。通过定时任务增加时间戳,避免每次都使用System获取当前时间。可以看到Sentinel在性能方面所做的努力。Sentinel尽量减少对应用程序的影响。Sentinel会为每一个资源(接口)分别创建一个1分钟内保存1秒时间窗的Bucket数组和1秒内保存500ms时间窗的Bucket数组,并将这两个数组包装成一个Node来进行统计请求该接口的数据。每个Bucket记录一个时间窗口内的请求总数、失败总数、总耗时(平均耗时可以通过总耗时计算)、限制的请求总数或吹。所以Sentinel消耗的内存至少是资源总数乘以每个资源对应的Node占用的内存大小。每个Node占用的内存大小是一个大小为2的Bucket数组和一个大小为60的Bucket数组。内存。Sentinel工作流源码分析Sentinel通过复用Bucket降低内存消耗,使用LongAdder降低并发统计数据的性能消耗。除了这些,Sentinel通过责任链模式实现了统计、限流、熔断降级等功能,实现了局部Lock-free。Sentinel的基本使用:Sentinel通过ProcessorSlot实现统计、限流、熔断降级等功能。例如,资源当前时间窗口内的请求总数和失败次数的统计由StatisticSlot完成,flowSlot用于判断是否需要限制当前请求,DegradeSlot用于判断当前请求是否需要降级。这些ProcessorSlots按照严格的顺序打包成一个链表,比如StatisticSlot在FlowSlot之前,FlowSlot在DegradeSlot之前。ProcessorSlot的入口方法在收到客户端请求时或客户端向服务器发送请求之前调用,出口方法在服务器处理完请求(包括异常完成)或客户端发送请求时调用。每个ProcessorSlot通过fireEntry方法或者fireExit方法向下传递信号。看过Netty源码的朋友应该对这种设计模式的使用不陌生。Netty也采用责任链模型,将处理请求的Handler封装成一个链表,实现对请求的部分串行处理。不过Sentinel的ProcessorSlot和Netty的Handler有些不同。ProcessorSlot的exit方法并没有像Netty那样从后往前传递。我们熟悉的Shiro也是通过责任链(filter)实现的,那么Sentinel实现限流和断路器也就不难理解了。不考虑集群限流。当调用SphU的入口方法时,至少会经过StatisticSlot、FlowSlot、DegradeSlot三个ProcessorSlot。时序图如下。当调用StatisticSlot的入口方法时,StatisticSlot根据资源获取资源的Node,根据当前时间戳从Node获取当前时间窗口的Bucket,然后对该Bucket的请求总数递增1、StatisticSlot在入口方法中捕获异常。如果下游ProcessorSlot抛出的异常是BlockException或者BlockException的子类,则Bucket限流总数加1,否则Bucket异常总数加1。当调用FlowSlot的入口方法时,检查当前资源配置的限流规则是否满足限流条件,如果满足则抛出BlockException,表示当前请求限流。由于Sentinel支持集群限流,所以限流的实现比较复杂,暂不讨论。如果是单节点限流,实现方式类似于断路器降级的实现方式。本文只介绍断路器降级的实现。当调用DegradeSlot的入口方法时,检查当前资源配置的断路器降级规则是否满足条件,如果满足条件则抛出DegradeException,表示当前请求中断。Sentinel断路器降级实现源码分析Sentinel会为每个资源创建一个Node(ResourceWrapper)来统计请求数据(请求总数、异常总数、限流或断路器总数、总耗时),以及降级为电流限制和断路器功能提供支持。ResourceWrapper的名字就是资源名,也可以理解为接口url,但是这种理解是不正确的。我们在配置限流规则或者熔断降级规则的时候也会用到资源名称。ResourceWrapper的entryType是流量类型,可能的值有IN和OUT。IN表示流入类型,即服务端接收客户端请求;OUT为流出型,即客户端向服务端发起请求。ResourceWrapper的resourceType是资源类型,表示是Servlet还是RPC、API网关、数据库等,可见Sentinel也支持对数据库的访问限流熔断。Sentinel提供的熔断降级功能不仅可以在client端使用,也可以在server端使用,但是对于流量类型为OUT的资源降级,一般是放在client端,以保证不受影响服务器端。不要被服务器拖累。判断Sentinel的熔断降级功能是否支持服务端执行,我们可以阅读DegradeSlot的源码,查看是否限制只有流量类型为EntryType.OUT时才生效。当调用DegradeSlot的入口方法时,DegradeSlot调用DegradeRuleManager的checkDegrade方法检查当前请求是否满足断路器降级规则。在学习使用Sentinel实现熔断降级的时候,我们使用DegradeRuleManager来加载我们配置的熔断降级规则,所以DegradeSlot将校验逻辑交给了DegradeRuleManager来完成。DegradeRuleManager首先根据资源名称获取配置的断路器降级规则,因为我们可以为同一个资源配置多个断路器降级规则,所以返回的将是一个集合。然后遍历熔断器降级规则,调用DegradeRule的passCheck方法,将检查是否需要触发熔断器的逻辑交给DegradeRule。如果一个资源配置了多个熔断器降级规则,只要有一个熔断器降级规则满足条件,就会触发熔断器。DegradeRule的passCheck方法源码如下。从DegradeRule的passCheck方法的源码中,我们没有找到任何地方限制只有流量类型为EntryType.OUT时才触发熔断器降级。因此,断路器降级不仅可以用在客户端,也可以用在服务端。断路器降级策略支持三种类型:1.平均响应时间(DEGRADE_GRADE_RT)2.异常比率(DEGRADE_GRADE_EXCEPTION_RATIO)3.异常数量(DEGRADE_GRADE_EXCEPTION_COUNT)。官方文档在引入DEGRADE_GRADE_EXCEPTION_COUNT策略的地方增加了使用说明:注意由于统计的时间窗口是分钟级的,如果timeWindow小于60s,可能在结束后仍会进入熔断状态保险丝状态。这句话不难理解。从DegradeRule的passCheck方法的源码中可以找到答案,如下图所示。本文转载自微信公众号“爪哇艺术”,可通过以下二维码关注。转载本文请联系爪哇艺术公众号。
