Sentinel源码分析(四) - 限流规则

本文详细剖析了Sentinel中的FlowRule结构,包括其元数据如grade、controlBehavior和count,并深入讲解了FlowRuleChecker如何检查规则和DefaultController的限流算法。重点探讨了直接拒绝策略,展示了配置与实现细节。
摘要由CSDN通过智能技术生成

@Author:zxw
@Email:502513206@qq.com


目录

  1. Sentinel源码分析(一) - 初识Sentinel
  2. Sentinel源码分析(二) - Entry构建
  3. Sentinel源码分析(三) - 调用链路

1.前言

通过上篇文章已经知道FlowSlot节点是用来处理我们流控的实现,以下为添加规则的示例代码

private static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        // 资源名
        rule.setResource("HelloWorld");
        // 限流类型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // Set limit QPS to 20.
        // 限流阔值
        rule.setCount(1);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
}

以下是Sentinel控制台提供给我们的配置界面
在这里插入图片描述

2.源码分析

2.1 FlowRule

在查看Sentinel是如何实现流控之前,首先对FlowRule进行了解,单从实例代码来看一个FlowRule对象就代表一条规则,而FlowRuleManager则是我们规则的管理类了。先来看看Rule类的结构
在这里插入图片描述

可以发现有非常多的实现,不过对于流控的实现类则是FlowRule对象。在Sentinel中资源是一切的根本,所以在AbstractRule父类中定义了两个公共字段

private String resource;
private String limitApp;

对于limitApp则是应用的来源,如果不存在的话,那么FlowRule会提供一个默认的来源

public static final String LIMIT_APP_DEFAULT = "default";

 public FlowRule() {
        super();
        setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
 }

此外,在Sentinel有两种限流的方式,分别为qps和线程数量,在FlowRule中使用grade字段进行标识,默认为qps限流。

// RuleConstant
public static final int FLOW_GRADE_THREAD = 0;
public static final int FLOW_GRADE_QPS = 1;
// FlowRule
private int grade = RuleConstant.FLOW_GRADE_QPS;

通过Sentinel控制台提供的功能来看,Sentinel有3种流控效果

  1. 直接失败
  2. warm up
  3. 排队等待
  4. 2与3的结合

默认情况,使用的是直接拒绝方式,通过controlBehavior字段标识

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;

private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;

配置了流控效果,那么就有不同的实现类去实现不同的流控策略,Sentinel通过TrafficShapingController接口来实现不同的流控策略。

private TrafficShapingController controller;

所对应的实现则与上面配置的4个值相对应
在这里插入图片描述

既然是对流量进行控制,那么应该还需要记录当前流量值

private double count;

以上就是FlowRule对象所涉及到的一些元数据,我们主要关注的字段有如下几个

  1. grade:配置qps或者线程数量模式
  2. controlBehavior:配置流控效果
  3. count:配置请求阈值

流控调度大概如下图

在这里插入图片描述

2.2 FlowRuleChecker

该类是规则检查类,所做的就是拿到该资源对应的所有规则,然后遍历并校验规则。

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);
                }
            }
        }
    }

在取到每一个规则后,调用canPassCheck方法进行规则的校验,取到了资源的Node以后,获取到限流controller调用对应流控规则的限流算法

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);
    }

3.限流算法

3.1 DefaultController(直接拒绝)

如果我们想使用并发线程数来进行流量控制,那么所使用的限流算法只能是DefaultController,因为只有在该类中对并发线程数进行了判断处理。
首先思考一个问题,要想知道当前是否需要限流是不是首先得知道当前系统的调用程度。那么这里就会用到qps或者线程数量

int curCount = avgUsedTokens(node);

private int avgUsedTokens(Node node) {
        if (node == null) {
            return DEFAULT_AVG_USED_TOKENS;
        }
        return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());
}

如果当前请求数小于限制的数量总数,那么我们直接放行即可

if (curCount + acquireCount > count) {
    // .... 逻辑
    return false;
}
return true;

不过Sentinel内部还有一个针对请求优先级的算法,有兴趣的可以自己研究

if (curCount + acquireCount > count) {
            if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {
                long currentTime;
                long waitInMs;
                currentTime = TimeUtil.currentTimeMillis();
                waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);
                if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {
                    node.addWaitingRequest(currentTime + waitInMs, acquireCount);
                    node.addOccupiedPass(acquireCount);
                    sleep(waitInMs);

                    // PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}.
                    throw new PriorityWaitException(waitInMs);
                }
            }
            return false;
}

4.总结

这里只对直接拒绝策略的算法粗略的看了下,剩余其他算法感兴趣的可以直接研究,本篇主要分析了FlowRule的元数据结构

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值