Sentinel源碼分析(第四篇):部分功能插槽原理分析

1. 前言

在以前的文章中,分析了Sentinel如何統計數據指標的。本篇文章主要是分析調用鏈路上的一些功能插槽的功能和實現,包括系統自適應、黑白名單控制、流量控制、熔斷降級四個功能。node

2. Sentinel功能插槽規則

2.1. 規則的定義

在分析各類功能插槽以前,先看一下Sentinel對規則的定義。git

Sentinel使用Rule表示規則,Rule是一個接口,Rule只有一個方法,passCheck(),表示規則是否經過。github

AbstractRule實現了Rule,是一個抽象類,裏面定義了這個規則屬於的資源,限制的app。全部具體的規則都是繼承該規則。api

2.2. 規則的加載或更新

當定義了具體的規則以後,須要加載規則到系統中,這個加載過程是由規則管理器負責的,好比定義了系統自適應規則SystemRule,會由對應的規則管理器SystemRuleManager加載該規則。 下面就以SystemRuleManager爲例,分析規則的加載。緩存

每個規則管理器中都會有一個繼承了SimplePropertyListener規則改變處理器做爲變量,當規則改變以後,作相關的更新處理工做。每一個規則管理器中會定義一個內部類做爲該變量的具體實現,好比SystemRuleManager:bash

//建立一個文件處理器
private final static SystemPropertyListener listener = new SystemPropertyListener();

//定義一個處理該規則的處理器類
static class SystemPropertyListener extends SimplePropertyListener<List<SystemRule>>{
。。。
}

複製代碼

能夠看到,SystemRuleManager建立了一個SystemPropertyListener做爲文件改變的處理器,SystemPropertyListener是其內部的一個內部類。併發

規則管理器中還會定義一個SentinelProperty,SentinelProperty表明Sentinel中的配置信息,在這個地方就是表明配置的規則。而後會將定義的規則處理器添加給規則,當規則變動的時候,使用對應的規則處理器進行處理。app

private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();

currentProperty.addListener(listener);

複製代碼

以SystemRule更新爲例,當定義了規則以後,調用SystemRuleManager的loadRules()會進行規則的更新。ide

public static void loadRules(List<SystemRule> rules) {
    currentProperty.updateValue(rules);
}
複製代碼

調用loadRules()的時候實際上是調用DynamicSentinelProperty的updateValues()方法:ui

public boolean updateValue(T newValue) {
    if (isEqual(value, newValue)) {
        return false;
    }
    RecordLog.info("[DynamicSentinelProperty] Config will be updated to: " + newValue);

    value = newValue;
    for (PropertyListener<T> listener : listeners) {
        listener.configUpdate(newValue);
    }
    return true;
}
複製代碼

能夠看到,DynamicSentinelProperty的updateValues()的方法中實際上是調用規則改變處理器的configUpdate()方法,因此具體是如何更新的是每一個規則管理器裏面定義的內部類SimplePropertyListener。

以上就是規則的加載過程,各類規則的加載大體流程是同樣的,只是具體的加載邏輯都是由本身的規則管理器中定義的SimplePropertyListener來負責更新。

3. 系統自適應限流

3.1. 什麼是系統自適應限流

系統保護規則是從應用級別的入口流量進行控制,從單臺機器的 load、CPU 使用率、平均 RT、入口 QPS 和併發線程數等幾個維度監控應用指標,讓系統儘量跑在最大吞吐量的同時保證系統總體的穩定性。

系統保護規則是應用總體維度的,而不是資源維度的,而且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IN),好比 Web 服務或 Dubbo 服務端接收的請求,都屬於入口流量。

系統規則支持如下的模式:

  • Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 做爲啓發指標,進行自適應系統保護。當系統 load1 超過設定的啓發值,且系統當前的併發線程數超過估算的系統容量時纔會觸發系統保護(BBR 階段)。系統容量由系統的 maxQps * minRt 估算得出。設定參考值通常是 CPU cores * 2.5。

  • CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過閾值即觸發系統保護(取值範圍 0.0-1.0),比較靈敏。

  • 平均 RT:當單臺機器上全部入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。 併發線程數:當單臺機器上全部入口流量的併發線程數達到閾值即觸發系統保護。

  • 入口 QPS:當單臺機器上全部入口流量的 QPS 達到閾值即觸發系統保護。

以上文字摘抄至sentinel官方文檔。

2.2. SystemRule

SystemRule表明系統自適應保護的規則定義,定義了load、cpu、qps、rt、thread維度的參數。

public class SystemRule extends AbstractRule {
    /**
     * negative value means no threshold checking.
     */
    private double highestSystemLoad = -1;
    /**
     * cpu usage, between [0, 1]
     */
    private double highestCpuUsage = -1;
    private double qps = -1;
    private long avgRt = -1;
    private long maxThread = -1;
}
複製代碼

2.3. SystemRuleManager

SystemRuleManager是實現系統自適應保護的核心類,負責管理系統自適應規則,包括加載更新規則,檢查當前入口調用是否知足規則。

public class SystemRuleManager {

    private static volatile double highestSystemLoad = Double.MAX_VALUE;
    private static volatile double highestCpuUsage = Double.MAX_VALUE;
    private static volatile double qps = Double.MAX_VALUE;
    private static volatile long maxRt = Long.MAX_VALUE;
    private static volatile long maxThread = Long.MAX_VALUE;
    
    private static volatile boolean highestSystemLoadIsSet = false;
    private static volatile boolean highestCpuUsageIsSet = false;
    private static volatile boolean qpsIsSet = false;
    private static volatile boolean maxRtIsSet = false;
    private static volatile boolean maxThreadIsSet = false;

    /**
     * 是否存在指定的系統規則,便是否調用SystemRuleManager.loadRules()加載規則
     */
    private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);

    /**
     * 一個獲取系統當前的load和cpu使用的任務
     */
    private static SystemStatusListener statusListener = null;

    /**
     * 系統規則改變後的處理器,主要將規則設置爲指定的規則
     */
    private final static SystemPropertyListener listener = new SystemPropertyListener();
    
      /**
     * 系統規則改變後的處理器,主要將規則設置爲指定的規則
     */
    private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();

    /**
     * 
     */
    private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
        new NamedThreadFactory("sentinel-system-status-record-task", true));

    static {
        checkSystemStatus.set(false);
        statusListener = new SystemStatusListener();
        //定時任務5秒後開始,每秒執行一次
        scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
        currentProperty.addListener(listener);
    }
    
    ...
}
複製代碼

上面的參數主要含義爲:

  • 前面定義的十個參數主要是描述系統自適應保護參數的。
  • checkSystemStatus:是否須要進行系統自適應保護。
  • statusListener:負責獲取系統運行參數的一個任務。
  • listener:系統規則改變後的處理器。
  • currentProperty:當前配置文件。
  • scheduler:一個負責運行statusListener的定時任務。

再看靜態代碼塊,主要是初始化scheduler,設置運行的任務和頻率,而且將系統規則處理器添加給currentProperty。

SystemRuleManager中會負責規則的加載,這個部分在第二小節中已經講過。

SystemRuleManager中還有一個最重要的方法,checkSystem(),負責校驗進入的請求是否知足系統自適應設置的規則。

public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
    // Ensure the checking switch is on.
    //若是沒有加入系統規則,則不須要檢查
    if (!checkSystemStatus.get()) {
        return;
    }

    // for inbound traffic only
    //若是不是系統入口的資源,不檢查
    if (resourceWrapper.getType() != EntryType.IN) {
        return;
    }

    //根據全局的ClusterNode獲取數據指標,ClusterNode是一個類變量,全局惟一,類加載後生成。
    // 每次經過check後再StatisticSlot中累加統計

    // total qps
    double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
    if (currentQps > qps) {
        throw new SystemBlockException(resourceWrapper.getName(), "qps");
    }

    // total thread
    int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
    if (currentThread > maxThread) {
        throw new SystemBlockException(resourceWrapper.getName(), "thread");
    }

    double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
    if (rt > maxRt) {
        throw new SystemBlockException(resourceWrapper.getName(), "rt");
    }

    // load. BBR algorithm.
    //若是當前機器的load大於設置的值
    if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
        if (!checkBbr(currentThread)) {
            throw new SystemBlockException(resourceWrapper.getName(), "load");
        }
    }

    // cpu usage
    if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
        if (!checkBbr(currentThread)) {
            throw new SystemBlockException(resourceWrapper.getName(), "cpu");
        }
    }
}
複製代碼

checkSystem()中具體的邏輯如上,具體實現原理能夠參考官方文檔。

2.4. SystemSlot

SystemSlot是負責系統自適應保護的功能插槽,每個內部entry進入的時候都會進行系統自適應保護校驗。SystemSlot很簡單,主要是在entry()方法中調用 SystemRuleManager.checkSystem()方法進行系統自適應保護的檢查,具體邏輯仍是在SystemRuleManager中。

3. AuthoritySlot

黑白名單根據資源的請求來源(origin)限制資源是否經過,若配置白名單則只有請求來源位於白名單內時纔可經過;若配置黑名單則請求來源位於黑名單時不經過,其他的請求經過。

3.1. AuthorityRule

AuthorityRule表明對黑白名單設置的規則,其中主要包括個屬性設置,一個是限制的來源名稱,多個來源使用逗號分隔。還須要設置限制的時白名單仍是黑名單。

private String limitApp;
private int strategy = RuleConstant.AUTHORITY_WHITE;
複製代碼

3.2. AuthorityRuleManager

AuthorityRuleManager和上一節的SystemRuleManager相似,是負責管理黑白名單的加載的。先看一下其中的屬性定義:

public final class AuthorityRuleManager {

    /**
     * 保存權限配置,以resource爲key,因此同一個resource只能有一個最新的權限規則
     */
    private static Map<String, Set<AuthorityRule>> authorityRules = new ConcurrentHashMap<>();

    /**
     * 規則改變處理器
     */
    private static final RulePropertyListener LISTENER = new RulePropertyListener();

    /**
     * 當前規則
     */
    private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();

    static {
        currentProperty.addListener(LISTENER);
    }
}
複製代碼

和SystemRuleManager相似,AuthorityRuleManager也有一個規則改變的處理器RulePropertyListener,這個RulePropertyListener是AuthorityRuleManager的一個內部類,而後也有一個表示當前配置的配置類currentProperty,當須要加載更新規則的時候,邏輯和SystemRuleManager相似,因此再也不描述。

3.3. AuthoritySlot

AuthoritySlot是負責處理黑白名單的功能插槽,當entry進入的時候,調用checkBlackWhiteAuthority()進行校驗。

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
    throws Throwable {
    checkBlackWhiteAuthority(resourceWrapper, context);
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

/**
 * 檢查黑白名單
 * @param resource
 * @param context
 * @throws AuthorityException
 */
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {

    //獲取全部的規則
    Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();

    if (authorityRules == null) {
        return;
    }

    //獲取指定resource的規則
    Set<AuthorityRule> rules = authorityRules.get(resource.getName());
    if (rules == null) {
        return;
    }

    for (AuthorityRule rule : rules) {
        if (!AuthorityRuleChecker.passCheck(rule, context)) {
            throw new AuthorityException(context.getOrigin(), rule);
        }
    }
}
複製代碼

能夠看到,這個地方主要是獲取全部的黑白名單規則,而後遍歷調用AuthorityRuleChecker.passCheck()進行校驗,校驗的具體邏輯很簡單,就不在此描述了。

4. 流量控制

流量控制原理是監控應用流量的 QPS 或併發線程數等指標,當達到指定的閾值時對流量進行控制,以免被瞬時的流量高峯沖垮,從而保障應用的高可用性。

4.1. FlowRule

FlowRule表明流量控制規則,定義了流量控制的各類參數。

public class FlowRule extends AbstractRule {
    /**
     * The threshold type of flow control (0: thread count, 1: QPS).
     */
    private int grade = RuleConstant.FLOW_GRADE_QPS;

    /**
     * Flow control threshold count.
     */
    private double count;

    /**
     * Flow control strategy based on invocation chain.
     *
     * {@link RuleConstant#STRATEGY_DIRECT} for direct flow control (by origin);
     * {@link RuleConstant#STRATEGY_RELATE} for relevant flow control (with relevant resource);
     * {@link RuleConstant#STRATEGY_CHAIN} for chain flow control (by entrance resource).
     */
    private int strategy = RuleConstant.STRATEGY_DIRECT;

    /**
     * Reference resource in flow control with relevant resource or context.
     */
    private String refResource;

    /**
     * Rate limiter control behavior.
     * 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter
     */
    private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;

    private int warmUpPeriodSec = 10;

    /**
     * Max queueing time in rate limiter behavior.
     */
    private int maxQueueingTimeMs = 500;

    private boolean clusterMode;
    /**
     * Flow rule config for cluster mode.
     */
    private ClusterFlowConfig clusterConfig;

    /**
     * The traffic shaping (throttling) controller.
     */
    private TrafficShapingController controller;
複製代碼

一條限流規則主要由下面幾個因素組成,咱們能夠組合這些元素來實現不一樣的限流效果:

  • resource:資源名,即限流規則的做用對象
  • count: 限流閾值
  • grade: 限流閾值類型(QPS 或併發線程數)
  • limitApp: 流控針對的調用來源,若爲 default 則不區分調用來源
  • strategy: 調用關係限流策略
  • controlBehavior: 流量控制效果(直接拒絕、Warm Up、勻速排隊)

4.2. FlowSlot

FlowSlot是負責流量控制的功能插槽,具體實現以下:

* 流量規則檢查器
     */
    private final FlowRuleChecker checker;

    public FlowSlot() {
        this(new FlowRuleChecker());
    }
    
  ...
  
     @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);
    }

    void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
        throws BlockException {
        checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
    }


    /**
     * 規則提供者
     */
    private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
        @Override
        public Collection<FlowRule> apply(String resource) {
            // Flow rule map should not be null.
            Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
            return flowRules.get(resource);
        }
    };
    ...
複製代碼

FlowSLot中定義了一個FlowRuleChecker,FlowRuleChecker負責對限流規則進行檢查。能夠看到,FlowSlot實際的限流邏輯調用FlowRuleChecker實現的。在調用FlowRuleChecker的checkFlow()方法的時候,須要傳入一個ruleProvider,這是一個Function。

4.3. FlowRuleChecker

上面說到FlowRuleChecker是負責流量控制校驗的,FlowSlot中調用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;
    }
    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);
            }
        }
    }
}
複製代碼

checkFlow()很簡單,就是獲取限流規則,遍歷調用canPassCheck()方法進行校驗,若是校驗失敗,則拋出FlowException。

public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                boolean prioritized) {
    String limitApp = rule.getLimitApp();
    if (limitApp == null) {
        return true;
    }
    
    if (rule.isClusterMode()) {
        return passClusterCheck(rule, context, node, acquireCount, prioritized);
    }
    
    return passLocalCheck(rule, context, node, acquireCount, prioritized);
}
複製代碼

canPassCheck()也很簡單,就是根據不一樣的模式調用不一樣的方法。Sentinel可使用集羣模式運行或者本地模式,不一樣的模式限流邏輯不同。這個地方因爲沒有講到集羣,因此先按本地模式進行分析。

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);
}
複製代碼

passLocalCheck()中,先根據配置的strategy和請求來源,得到對應的Node節點,再調用對應節點的canPass()方法進行校驗。

在FlowRule中有一個TrafficShapingController,表明流量控制的類型,包括默認值、直接拒絕、Warm Up、勻速排隊,因此具體如何限流須要看設置的策略。

4.4. TrafficShapingController

TrafficShapingController是一個接口,主要是定義流量控制策略,它有三個實現,分別表明不一樣的處理模式:

  • DefaultController:默認處理策略,直接拒絕處理
  • RateLimiterController:勻速排隊
  • WarmUpController:預熱/冷啓動方式
  • WarmUpRateLimiterController:預熱+勻速排隊

在FlowRule中會有一個TrafficShapingController類型的變量rater,這個rater是在更新規則的時候根據設置的規則建立出來的,具體以下:

private static TrafficShapingController generateRater(/*@Valid*/ FlowRule rule) {
    if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
        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());
}
複製代碼

5. 熔斷降級

Sentinel熔斷降級會在調用鏈路中某個資源出現不穩定狀態的時候對該資源進行限制,讓請求快速失敗,避免影響其餘的資源而致使級聯錯誤。當資源被降級後,在接下來的降級時間窗口內,對該資源的調用都會自動熔斷。

5.1. 降級策略

  • 平均響應時間 (DEGRADE_GRADE_RT):當 1s 內持續進入 5 個請求,對應時刻的平均響應時間(秒級)均超過閾值(count,以 ms 爲單位),那麼在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地熔斷(拋出 DegradeException)。注意 Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算做 4900 ms,若須要變動此上限能夠經過啓動配置項 -Dcsp.sentinel.statistic.max.rt=xxx 來配置。

  • 異常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):當資源的每秒請求量 >= 5,而且每秒異常總數佔經過量的比值超過閾值(DegradeRule 中的 count)以後,資源進入降級狀態,即在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地返回。異常比率的閾值範圍是 [0.0, 1.0],表明 0% - 100%。

  • 異常數 (DEGRADE_GRADE_EXCEPTION_COUNT):當資源近 1 分鐘的異常數目超過閾值以後會進行熔斷。注意因爲統計時間窗口是分鐘級別的,若 timeWindow 小於 60s,則結束熔斷狀態後仍可能再進入熔斷狀態

5.2. DegradeRule

public class DegradeRule extends AbstractRule {

    /**
     * RT threshold or exception ratio threshold count.
     */
    private double count;

    /**
     * Degrade recover timeout (in seconds) when degradation occurs.
     */
    private int timeWindow;

    /**
     * Degrade strategy (0: average RT, 1: exception ratio, 2: exception count).
     */
    private int grade = RuleConstant.DEGRADE_GRADE_RT;

    /**
     * 每秒鐘連續進入的請求超的平均響應時間超過閥值的請求數
     * Minimum number of consecutive slow requests that can trigger RT circuit breaking.
     *
     * @since 1.7.0
     */
    private int rtSlowRequestAmount = RuleConstant.DEGRADE_DEFAULT_SLOW_REQUEST_AMOUNT;

    /**
     * 每秒鐘連續進入的請求發生異常的請求數
     * Minimum number of requests (in an active statistic time span) that can trigger circuit breaking.
     *
     * @since 1.7.0
     */
    private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;
}
複製代碼

DeGradeRule表明熔斷降級的規則,各個字段含義爲:

  • grade:熔斷降級的模式,有平均響應時間、異常比例、異常數。
  • count:發生熔斷降級的閾值。
  • timeWindow:發生熔斷降級後持續的時間。
  • minRequestAmount:每秒連續進入的請求發生異常不熔斷降級的最小閾值。
  • rtSlowRequestAmount:每秒連續進入的請求平均響應時間超過閾值的數量。

5.3. DegradeSlot

DegradeSlot是負責處理熔斷降級的功能插槽,其代碼很是簡單,由於具體熔斷降級的判斷是在DegradeRuleManager中實現的。

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
    throws Throwable {
    DegradeRuleManager.checkDegrade(resourceWrapper, context, node, count);
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
複製代碼

5.4. DegradeRuleManager

DegradeRuleManager主要負責加載熔斷降級規則、對調用entry進行熔斷降級校驗。

/**
 * 保存降級規則的緩存,以resource爲key
 */
private static final Map<String, Set<DegradeRule>> degradeRules = new ConcurrentHashMap<>();

/**
 * 規則改變處理器
 */
private static final RulePropertyListener LISTENER = new RulePropertyListener();


private static SentinelProperty<List<DegradeRule>> currentProperty
    = new DynamicSentinelProperty<>();

static {
    currentProperty.addListener(LISTENER);
}
複製代碼

DegradeRuleManager使用一個map來保存不一樣資源加載的熔斷降級規則,而且也擁有本身的規則改變處理器,規則的加載和更新邏輯與其餘功能插槽相似。

從上面能夠看出,當entry進入DegradeSlot的時候,其實是調用DegradeRuleManager的checkDegrade()方法進行熔斷降級的檢查。

public static void checkDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count)
    throws BlockException {

    Set<DegradeRule> rules = degradeRules.get(resource.getName());
    if (rules == null) {
        return;
    }

    for (DegradeRule rule : rules) {
        if (!rule.passCheck(context, node, count)) {
            throw new DegradeException(rule.getLimitApp(), rule);
        }
    }
}
複製代碼

checkDegrade()方法先根據resource獲取對應的限流規則,而後循環調用規則的passCheck()方法進行檢查,若是不能經過檢查,則拋出DegradeException。

因此,具體的校驗邏輯是在DegradeRule中的passCheck()實現的,具體代碼以下:

public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
    //若是是在限流的窗口時間內,直接降級
    if (cut.get()) {
        return false;
    }

    //降級只針對resource維度進行,不區分context,不區分orgin,在statisticSlot中會在defaultNode裏面對clusterNode進行累加
    ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
    if (clusterNode == null) {
        return true;
    }

    if (grade == RuleConstant.DEGRADE_GRADE_RT) {

        //case1:rt降級
        //平均響應時間
        double rt = clusterNode.avgRt();

        //平均響應時間小於設置的閥值,直接經過
        if (rt < this.count) {
            passCount.set(0);
            return true;
        }
        
        //請求數自增,若是請求數小於設置的值5,直接經過
        if (passCount.incrementAndGet() < rtSlowRequestAmount) {
            return true;
        }
        
    } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {

        //case2 異常比例

        double exception = clusterNode.exceptionQps();
        double success = clusterNode.successQps();
        double total = clusterNode.totalQps();
        
        //若是請求總數小於設置的每秒容許的最小請求數量,直接返回
        if (total < minRequestAmount) {
            return true;
        }
        double realSuccess = success - exception;
        if (realSuccess <= 0 && exception < minRequestAmount) {
            return true;
        }

        if (exception / success < count) {
            return true;
        }
    } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {

        //case3: 異常數

        //統計異常總數的時間窗口是分鐘級別的,若是timeWindow的時間小於60s,會以60s進行熔斷
        
        double exception = clusterNode.totalException();
        //若是異常數小於指定的值,直接返回
        if (exception < count) {
            return true;
        }
    }

    //開啓一個定時任務,在指定的窗口時間後執行,將請求數設置爲0,降級標識設置爲false
    if (cut.compareAndSet(false, true)) {
        ResetTask resetTask = new ResetTask(this);
        pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
    }

    return false;
}
複製代碼

passCheck()邏輯比較簡單,主要是先根據以前獲取的數據判斷是否知足降級設置的閾值,若是超過,則返回false,而且開啓一個線程,其做用就是發生降級後指定的timeWindow內直接將後續請求當作降級處理。

6. 小結

這篇文章主要分析Sentinel中經常使用的功能插槽的實現原理,瞭解Sentinel的限流降級策略。

7.參考資料

流量控制
熔斷降級
系統自適應限流
黑白名單控制

相關文章
相關標籤/搜索