Sentinel中的责任链模式:Sentinel整体工作流程

Sentinel对性能的消耗如何

Sentinel统计QPS使用的是滑动窗口:时间窗口+Bucket,通过循环复用Bucket以减少对内存的占用,在统计QPS时,更是利用当前时间戳定位Bucket,使用LongAdder统计时间窗口内的请求成功数、失败数、总耗时等指标数据优化了并发锁,通过定时任务递增时间戳避免每次都使用System获取当前时间。可以看到Sentinel在性能方面所做出的努力,Sentinel尽最大可能降低自身对应用的影响。

Sentinel会为每个资源创建一个保存一分钟内时间窗口为1秒的Bucket数组以及一个保存一秒钟内以500ms为时间窗口的Bucket数组,将这两个数组包装为一个Node,以统计该接口的请求数据。每个Bucket记录一个时间窗口内的请求总数、失败总数、总耗时(通过总耗时可计算平均耗时)、被限流或者被熔断的请求总数这些指标数据。

Sentinel中的责任链模式:Sentinel整体工作流程_第1张图片

因此,Sentinel消耗的内存至少是资源总数乘以每个资源对应的Node占用的内存大小,每个Node占用的内存大小即为一个大小为2的Bucket数组和一个大小为60的Bucket数组所占用的内存。

Sentinel工作流程源码分析

Sentinel通过复用Bucket降低对内存的消耗,使用LongAdder降低并发统计数据对性能的消耗,除这些之外,Sentinel通过责任链模式实现统计、限流、熔断降级等功能,实现局部无锁化。

Sentinel的基本使用:

Sentinel中的责任链模式:Sentinel整体工作流程_第2张图片

Sentinel实现统计、限流、熔断降级等功能由一个个ProcessorSlot完成,例如,统计资源当前时间窗口的请求总数、失败总数等由StatisticSlot完成,判断当前请求是否需要限流由FlowSlot完成,判断当前请求是否需要熔断降级由DegradeSlot完成。

Sentinel中的责任链模式:Sentinel整体工作流程_第3张图片

这些ProcessorSlot按照严格的顺序包装成一个链表,比如StatisticSlot在FlowSlot之前,FlowSlot在DegradeSlot之前。

Sentinel中的责任链模式:Sentinel整体工作流程_第4张图片

ProcessorSlot的entry方法在接收到客户端请求时或者客户端向服务端发送请求之前被调用,exit方法则是在服务端处理完请求(包括异常完成)时或者客户端发送请求完成时被调用。每个ProcessorSlot通过fireEntry方法或者fireExit方法向下传递信号。

看过Netty源码的朋友应该对这种设计模式的使用并不陌生,Netty也是通过责任链模式将处理请求的Handler包装为链表,实现局部串行处理请求。但Sentinel的ProcessorSlot与Netty的Handler有些区别,ProcessorSlot的exit方法并不像Netty那样是从后往前传递的。

我们熟悉的Shiro也是通过责任链实现(过滤器),所以Sentinel实现限流、熔断并不难理解。在不考虑集群限流的情况下。当SphU的entry方法被调用时,至少会经过StatisticSlot、FlowSlot、DegradeSlot这三个ProcessorSlot,其时序图如下。

Sentinel中的责任链模式:Sentinel整体工作流程_第5张图片

当StatisticSlot的entry方法被调用时,由StatisticSlot根据资源获取资源的Node,根据当前时间戳从Node获取当前时间窗口的Bucket,然后将Bucket的请求总数自增1。StatisticSlot在entry方法中捕获异常,如果下游的ProcessorSlot抛出异常为BlockException或BlockException的子类,则将Bucket的限流总数自增1,否则将Bucket的异常总数自增1。

当FlowSlot的entry方法被调用时,检查为当前资源配置的限流规则是否满足限流条件,如果满足条件则抛出BlockException异常,表示当前请求被限流。由于Sentinel支持集群限流,所以限流的实现上比较复杂,我们暂不讨论。如果是单节点的限流,则实现上与熔断降级的实现差不多。

当DegradeSlot的entry方法被调用时,检查为当前资源配置的熔断降级规则是否满足条件,如果满足条件则抛出DegradeException异常,表示当前请求被熔断。

你可能感兴趣的:(Spring,Cloud)