StatisticSlot定义: StatisticSlot 是 Sentinel 最为重要的类之一,用于根据规则判断结果进行相应的统计操作。
entry 的时候:依次执行后面的判断 slot。每个 slot 触发流控的话会抛出异常(BlockException的子类)。若有 BlockException抛出,则记录 block 数据;若无异常抛出则算作可通过(pass),记录 pass 数据。
exit 的时候:若无 error(无论是业务异常还是流控异常),记录 complete(success)以及 RT,线程数-1。
记录数据的维度:线程数+1、记录当前 DefaultNode 数据、记录对应的 originNode 数据(若存在 origin)、累计 IN 统计数据(若流量类型为 IN)。
分析源码核心方法 首先我们从ClusterNodeSlot中的entry方法中找到调用下一节点方法,然后向下跟踪,道理和之前的都一样,调用的是父级方法
1 2 3 4 5 6 7 8 9 fireEntry(context, resourceWrapper, node, count, prioritized, args); @Override public void fireEntry (Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable { if (next != null ) { next.transformEntry(context, resourceWrapper, obj, count, prioritized, args); } }
找到transformEntry方法,然后此时的下一个节点必然是StatisticSlot
1 2 3 4 5 6 7 @SuppressWarnings("unchecked") void transformEntry (Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args) throws Throwable { T t = (T)o; entry(context, resourceWrapper, t, count, prioritized, args); }
然后我们来看StatisticSlot的核心方法,这里的核心思想就是:封装了基础的流量统计和获取方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void entry (Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try { fireEntry(context, resourceWrapper, node, count, prioritized, args); node.increaseThreadNum(); node.addPassRequest(count); }
看到这里以后,我们对StatisticSlot作用已经比较了解了,那么现在我们主要要分析这个位置,也就是说向下分析一下的节点
1 fireEntry(context, resourceWrapper, node, count, prioritized, args);
这里向下的节点调用方式和之前的都一样,所以剩下的节点非别为:
但是其实这里的ParamFlowSlot包括SystemSlot和AuthoritySlot我们在操作的时候其实就已经知道了它们就是根据不同维度进行统计和检测,那么这三个我们就不再详细分析,这条链路中我们要主要分析流控和熔断:FlowSlot、DegradeSlot
FlowSlot定义: 这个slot 主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:
指定应用生效的规则,即针对调用方限流的;
调用方为 other 的规则;
调用方为 default 的规则。
分析核心方法 进入到核心方法的方式和之前的都一样,所以我们直接来看
1 2 3 4 5 6 7 8 @Override public void entry (Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args); }
那我们先来分析checkFlow方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void checkFlow (ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { checker.checkFlow(ruleProvider, resource, context, node, count, prioritized); } public void checkFlow (Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { if (ruleProvider == null || resource == null ) { return ; } Collection<FlowRule> rules = ruleProvider.apply(resource.getName()); if (rules != null ) { for (FlowRule rule : rules) { if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException (rule.getLimitApp(), rule); } } } }
这里我们需要注意一下FlowRule(流控规则),它继承自AbstractRule,那我们来看一下AbstractRule其中的两个类型,分别是资源名称和资源的来源
1 2 private String resource;private String limitApp;
那么通过这里我们就可以得知,流控的规则设置其实就是通过这个FlowRule来完成的,同样它的数据来源,就是我们使用的Sentinel-dashboard,当然也可以通过代码来进行设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public class FlowRule extends AbstractRule { public FlowRule () { super (); setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); } public FlowRule (String resourceName) { super (); setResource(resourceName); setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); } private int grade = RuleConstant.FLOW_GRADE_QPS; private double count; private int strategy = RuleConstant.STRATEGY_DIRECT; private String refResource; private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT; private int warmUpPeriodSec = 10 ; private int maxQueueingTimeMs = 500 ; private boolean clusterMode; private ClusterFlowConfig clusterConfig; private TrafficShapingController controller; }
这里还涉及到两个算法:令牌桶算法和漏斗算法
令牌桶算法:令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
漏斗算法:漏桶算法思路很简单,请求先进入到漏桶里,漏桶以固定的速度出水,也就是处理请求,当水加的过快,则会直接溢出,也就是拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。