Sentinel源码解析系列:
1.Sentinel源码分析—FlowRuleManager加载规则做了什么?
2. Sentinel源码分析—Sentinel是如何进行流量统计的?
上回我们用基于并发数来讲了一下Sentinel的整个流程,这篇文章我们来讲一下Sentinel的QPS流量控制是如何实现的。
先上一个极简的demo,我们的代码就从这个demo入手:
public static void main(String[] args) { List<FlowRule> rules = new ArrayList<FlowRule>(); FlowRule rule1 = new FlowRule(); rule1.setResource("abc"); rule1.setCount(20); rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); rule1.setLimitApp("default"); rules.add(rule1); FlowRuleManager.loadRules(rules); Entry entry = null; try { entry = SphU.entry("abc"); //dosomething } catch (BlockException e1) { } catch (Exception e2) { // biz exception } finally { if (entry != null) { entry.exit(); } } }在这个例子中我们首先新建了一个FlowRule实例,然后调用了loadRules方法加载规则,这部分的代码都和基于并发数的流量控制的代码是一样的,想要了解的朋友可以去看看我的这一篇文章1.Sentinel源码分析—FlowRuleManager加载规则做了什么?,下面我们说说不一样的地方。
在调用FlowRuleManager的loadRules方法的时候会创建一个rater实例:
FlowRuleUtil#buildFlowRuleMap
//设置拒绝策略:直接拒绝、Warm Up、匀速排队,默认是DefaultController TrafficShapingController rater = generateRater(rule); rule.setRater(rater);我们进入到generateRater看一下是怎么创建实例的:
FlowRuleUtil#generateRater
private static TrafficShapingController generateRater(/*@Valid*/ FlowRule rule) { if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) { switch (rule.getControlBehavior()) { case RuleConstant.CONTROL_BEHAVIOR_WARM_UP: //warmUpPeriodSec默认是10 return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(), ColdFactorProperty.coldFactor); case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER: //rule.getMaxQueueingTimeMs()默认是500 return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount()); case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER: return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(), rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor); case RuleConstant.CONTROL_BEHAVIOR_DEFAULT: default: // Default mode or unknown mode: default traffic shaping controller (fast-reject). } } return new DefaultController(rule.getCount(), rule.getGrade()); }这个方法里面如果设置的是按QPS的方式来限流的话,可以设置一个ControlBehavior属性,用来做流量控制分别是:直接拒绝、Warm Up、匀速排队。
接下来的所有的限流操作全部在FlowSlot中进行,不熟悉Sentinel流程的朋友可以去看看我的这一篇文章:2. Sentinel源码分析—Sentinel是如何进行流量统计的?,这篇文章介绍了Sentinel的全流程分析,本文的其他流程基本都在这篇文章上讲了,只有FlowSlot部分代码不同。
接下来我们来讲一下FlowSlot里面是怎么实现QPS限流的
FlowSlot#entry
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); } void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { checker.checkFlow(ruleProvider, resource, context, node, count, prioritized); }FlowSlot在实例化的时候会实例化一个FlowRuleChecker实例作为checker。在checkFlow方法里面会继续调用FlowRuleChecker的checkFlow方法,其中ruleProvider实例是用来根据根据resource来从flowRules中获取相应的FlowRule。
我们进入到FlowRuleChecker的checkFlow方法中
FlowRuleChecker#checkFlow
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; } //返回FlowRuleManager里面注册的所有规则 Collection<FlowRule> rules = ruleProvider.apply(resource.getName()); if (rules != null) { for (FlowRule rule : rules) { //如果当前的请求不能通过,那么就抛出FlowException异常 if (!canPassCheck(rule, context, node, count, prioritized)) { throw new FlowException(rule.getLimitApp(), rule); } } } }这里是调用ruleProvider来获取所有FlowRule,然后遍历rule集合通过canPassCheck方法来进行过滤,如果不符合条件则会抛出FlowException异常。
我们跟进去直接来到passLocalCheck方法:
private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) { //节点选择 Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node); if (selectedNode == null) { return true; } //根据设置的规则来拦截 return rule.getRater().canPass(selectedNode, acquireCount, prioritized); }这个方法里面会选择好相应的节点后调用rater的canPass方法来判断是否需要阻塞。
Rater有四个,分别是:DefaultController、RateLimiterController、WarmUpController、WarmUpRateLimiterController,我们挨个分析一下。
其中DefaultController是直接拒绝策略,我们在上一篇文章中已经分析过了,这次我们来看看其他三个。
RateLimiterController匀速排队