@author:zxw
@email:502513206@qq.com
@ Jishou University
1.前言
限流可以说是高并发中比较重要的一个问题了,面试的时候也经常有关此类的问题,刚好最近在学习限流算法时,苦于找不到好的实现,想起以前用过的sentinel框架里面就有限流的功能,所以顺道来学习下sentinel中限流的实现。
2.源码解析
我们先来看看Sentinel怎么配置限流策略,在FlowRule对象中,通过配置controlBehavior
属性就可以指定限流策略了,默认使用默认的限流策略。
private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("addUser");
// 默认填写qps的阈值控制方式
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 配置限流策略
// Rate limiter control behavior. 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter
rule.setControlBehavior(2);
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
前面生成的废话源码,这边我们直接跳过,直接跳到最核心的ruleMap构建中
FlowRuleManager.buildFlowRuleMap
在这里会对限流规则rule对象进行一系列的校验,然后组装map,我们直接跳到TrafficShapingController rater = generateRater(rule);
代码,这里生成我们的限流类
public static <K> Map<K, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction,
Predicate<FlowRule> filter, boolean shouldSort) {
Map<K, List<FlowRule>> newRuleMap = new ConcurrentHashMap<>();
if (list == null || list.isEmpty()) {
return newRuleMap;
}
Map<K, Set<FlowRule>> tmpMap = new ConcurrentHashMap<>();
for (FlowRule rule : list) {
// 校验
if (!isValidRule(rule)) {
RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);
continue;
}
if (filter != null && !filter.test(rule)) {
continue;
}
if (StringUtil.isBlank(rule.getLimitApp())) {
// 默认使用default名称
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
// 指定我们的限流规则
TrafficShapingController rater = generateRater(rule);
rule.setRater(rater);
K key = groupFunction.apply(rule);
if (key == null) {
continue;
}
Set<FlowRule> flowRules = tmpMap.get(key);
if (flowRules == null) {
// Use hash set here to remove duplicate rules.
flowRules = new HashSet<>();
tmpMap.put(key, flowRules);
}
flowRules.add(rule);
}
Comparator<FlowRule> comparator = new FlowRuleComparator();
for (Entry<K, Set<FlowRule>> entries : tmpMap.entrySet()) {
List<FlowRule> rules = new ArrayList<>(entries.getValue());
if (shouldSort) {
// Sort the rules.
Collections.sort(rules, comparator);
}
newRuleMap.put(entries.getKey(), rules);
}
return newRuleMap;
}
往下看看限流规则生成,可以看到这里判断流量控制的方式是基于qps还是thread,默认为qps
private static TrafficShapingController generateRater(/*@Valid*/ FlowRule rule) {
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
// 根据我们配置的controlBehavior值来判断使用哪个限流方式
switch (rule.getControlBehavior()) {
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
// 预热
return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(),
ColdFactorProperty.coldFactor);
case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
// 流速
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());
现在我们可以来看看不同的限流策略对应哪些值了,根据代码来看一共有如下4种限流策略
public static final int CONTROL_BEHAVIOR_DEFAULT = 0; // 默认,超过限制拒绝
public static final int CONTROL_BEHAVIOR_WARM_UP = 1; // 预热
public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2; // 流速控制
public static final int CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3; // 预热与流速相结合
有关限流的接口为TrafficShapingController
,看看此接口的实现类就是我们上面对应的4中限流种类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzY5LanF-1610763638293)(Sentinel源码解析 - 限流算法种类简介.assets/image-20210115215418440.png)]
这下我们就知道了如果要配置指定的限流策略可以通过rule.setControlBehavior(2);
语句设置对应的限流方式,还可以通过rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
方法设置是QPS还是thread方式限流,有关具体限流的实现之后博文再讲。