Sentinel之Slots插槽源碼分析流控規(guī)則(五)

一、引子

前面介紹了SystemSlot(系統(tǒng)規(guī)則檢查)和AuthoritySlot(授權(quán)規(guī)則檢查)狡耻,下面接著分析FlowSlot墩剖。

FlowSlot 會根據(jù)預(yù)設(shè)的規(guī)則猴凹,結(jié)合前面 NodeSelectorSlot、ClusterNodeBuilderSlot岭皂、StatistcSlot 統(tǒng)計出來的實時信息進行流量控制郊霎。

限流的直接表現(xiàn)是在執(zhí)行 Entry nodeA = SphU.entry(資源名字) 的時候拋出 FlowException 異常。FlowException 是 BlockException 的子類蒲障,您可以捕捉 BlockException 來自定義被限流之后的處理邏輯歹篓。

同一個資源可以對應(yīng)多條限流規(guī)則。FlowSlot 會對該資源的所有限流規(guī)則依次遍歷揉阎,直到有規(guī)則觸發(fā)限流或者所有規(guī)則遍歷完畢庄撮。

一條限流規(guī)則主要由下面幾個因素組成,我們可以組合這些元素來實現(xiàn)不同的限流效果:

resource:資源名毙籽,即限流規(guī)則的作用對象
count: 限流閾值
grade: 限流閾值類型洞斯,QPS 或線程數(shù)
strategy: 根據(jù)調(diào)用關(guān)系選擇策略:直接、關(guān)聯(lián)坑赡、鏈路
clusterMode:是否集群模式
controlBehavior:流控效果:快速失敗烙如、WarmUP、排隊等候毅否、WarmUP+排隊等候
refResource:關(guān)聯(lián)資源

在dashborad中亚铁,可以設(shè)置資源的流控規(guī)則:如圖

流控規(guī)則

二 、基于QPS/并發(fā)數(shù)的流量控制

流量控制主要有兩種統(tǒng)計類型螟加,一種是統(tǒng)計線程數(shù)徘溢,另外一種則是統(tǒng)計 QPS。類型由 FlowRule.grade 字段來定義捆探。其中然爆,0 代表根據(jù)線程并發(fā)數(shù)量來限流,1 代表根據(jù) QPS 來進行流量控制黍图。其中線程數(shù)曾雕、QPS 值,都是由 StatisticSlot 實時統(tǒng)計獲取的助被。

可以通過下面的命令查看實時統(tǒng)計信息:

curl http://localhost:8719/cnode?id=resourceName

輸出內(nèi)容格式如下:

idx id   thread  pass  blocked   success  total Rt   1m-pass   1m-block   1m-all   exeption
2   abc647 0     46     0           46     46   1       2763      0         2763     0

其中:

thread: 代表當前處理該資源的線程數(shù)剖张;
pass: 代表一秒內(nèi)到來到的請求;
blocked: 代表一秒內(nèi)被流量控制的請求數(shù)量恰起;
success: 代表一秒內(nèi)成功處理完的請求修械;
total: 代表到一秒內(nèi)到來的請求以及被阻止的請求總和;
RT: 代表一秒內(nèi)該資源的平均響應(yīng)時間检盼;
1m-pass: 則是一分鐘內(nèi)到來的請求;
1m-block: 則是一分鐘內(nèi)被阻止的請求翘单;
1m-all: 則是一分鐘內(nèi)到來的請求和被阻止的請求的總和吨枉;
exception: 則是一秒內(nèi)業(yè)務(wù)本身異常的總和蹦渣。

2.1并發(fā)線程數(shù)流量控制

線程數(shù)限流用于保護業(yè)務(wù)線程數(shù)不被耗盡。例如貌亭,當應(yīng)用所依賴的下游應(yīng)用由于某種原因?qū)е路?wù)不穩(wěn)定柬唯、響應(yīng)延遲增加,對于調(diào)用者來說圃庭,意味著吞吐量下降和更多的線程數(shù)占用锄奢,極端情況下甚至導(dǎo)致線程池耗盡。
為應(yīng)對高線程占用的情況剧腻,業(yè)內(nèi)有使用隔離的方案拘央,比如通過不同業(yè)務(wù)邏輯使用不同線程池來隔離業(yè)務(wù)自身之間的資源爭搶(線程池隔離),或者使用信號量來控制同時請求的個數(shù)(信號量隔離)书在。

  • 線程池隔離:分配一個線程池來處理這些資源灰伟。當沒有更多的空閑線程池中,請求被拒絕而不影響其他資源。使用線程池的好處是,它可以當超時后優(yōu)雅地隔開儒旬,但它也給我們帶來線程上下文切換和額外的成本栏账。如果傳入的請求已經(jīng)在獨立的線程,例如:servelet請求,它將會幾乎兩倍于如果使用線程池線程計數(shù)。
  • 信號量隔離:在這個資源中使用信號量來控制線程的并發(fā)數(shù)栈源。

這種隔離方案雖然能夠控制線程數(shù)量挡爵,但無法控制請求排隊時間。當請求過多時排隊也是無益的甚垦,直接拒絕能夠迅速降低系統(tǒng)壓力茶鹃。Sentinel線程數(shù)限流不負責創(chuàng)建和管理線程池,而是簡單統(tǒng)計當前請求上下文的線程個數(shù)制轰,如果超出閾值前计,新的請求會被立即拒絕。

2.2QPS流量控制

當 QPS 超過某個閾值的時候垃杖,則采取措施進行流量控制男杈。流量控制的手段包括下面 4 種,對應(yīng) FlowRule 中的 controlBehavior 字段:

  • 直接拒絕(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式调俘。該方式是默認的流量控制方式伶棒,當QPS超過任意規(guī)則的閾值后,新的請求就會被立即拒絕彩库,拒絕方式為拋出FlowException肤无。這種方式適用于對系統(tǒng)處理能力確切已知的情況下,比如通過壓測確定了系統(tǒng)的準確水位時骇钦。
  • 冷啟動(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式宛渐。該方式主要用于系統(tǒng)長期處于低水位的情況下,當流量突然增加時,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮窥翩。通過"冷啟動"业岁,讓通過的流量緩慢增加,在一定時間內(nèi)逐漸增加到閾值上限寇蚊,給冷系統(tǒng)一個預(yù)熱的時間笔时,避免冷系統(tǒng)被壓垮的情況。
    通常冷啟動的過程系統(tǒng)允許通過的 QPS 曲線如下圖所示:
冷啟動
  • 勻速器(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式仗岸。這種方式嚴格控制了請求通過的間隔時間允耿,也即是讓請求以均勻的速度通過,對應(yīng)的是漏桶算法扒怖。
    該方式的作用如下圖所示:
    勻速器

這種方式主要用于處理間隔性突發(fā)的流量较锡,例如消息隊列。想象一下這樣的場景姚垃,在某一秒有大量的請求到來念链,而接下來的幾秒則處于空閑狀態(tài),我們希望系統(tǒng)能夠在接下來的空閑期間逐漸處理這些請求积糯,而不是在第一秒直接拒絕多余的請求掂墓。

  • 冷啟動+勻速器(RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER)方式。除了讓流量緩慢增加看成,還控制的了請求的間隔時間君编,讓請求已均勻速度通過。這種策略是1.4.0版本新增的川慌。

三吃嘿、基于調(diào)用關(guān)系的流量控制

調(diào)用關(guān)系包括調(diào)用方、被調(diào)用方梦重;方法又可能會調(diào)用其它方法兑燥,形成一個調(diào)用鏈路的層次關(guān)系。Sentinel 通過 NodeSelectorSlot 建立不同資源間的調(diào)用的關(guān)系琴拧,通過ClusterBuilderSlot設(shè)置每個資源源節(jié)點降瞳,并且通過 StatisticSlot 記錄每個資源的實時統(tǒng)計信息。

3.1 根據(jù)調(diào)用方限流

RuleConstant.STRATEGY_DIRECT = 0

ContextUtil.enter(resourceName, origin) 方法中的 origin 參數(shù)標明了調(diào)用方身份蚓胸。這些信息會在 StatisticSlot 中被統(tǒng)計挣饥。

限流規(guī)則中的 limitApp 字段用于根據(jù)調(diào)用方進行流量控制。該字段的值有以下三種選項沛膳,分別對應(yīng)不同的場景:

  • default:表示不區(qū)分調(diào)用者扔枫,來自任何調(diào)用者的請求都將進行限流統(tǒng)計。如果這個資源名的調(diào)用總和超過了這條規(guī)則定義的閾值锹安,則觸發(fā)限流短荐。
  • {some_origin_name}:表示針對特定的調(diào)用者倚舀,只有來自這個調(diào)用者的請求才會進行流量控制。例如 NodeA 配置了一條針對調(diào)用者caller1的規(guī)則搓侄,那么當且僅當來自 caller1 對 NodeA 的請求才會觸發(fā)流量控制瞄桨。
  • other:表示針對除 {some_origin_name} 以外的其余調(diào)用方的流量進行流量控制话速。例如讶踪,資源NodeA配置了一條針對調(diào)用者 caller1 的限流規(guī)則,同時又配置了一條調(diào)用者為 other 的規(guī)則泊交,那么任意來自非 caller1 對 NodeA 的調(diào)用乳讥,都不能超過 other 這條規(guī)則定義的閾值。

同一個資源名可以配置多條規(guī)則廓俭,規(guī)則的生效順序為:{some_origin_name} > other > default

3.2 具有關(guān)系的資源流量控制:關(guān)聯(lián)流量控制

RuleConstant.STRATEGY_RELATE = 1

當兩個資源之間具有資源爭搶或者依賴關(guān)系的時候云石,這兩個資源便具有了關(guān)聯(lián)。比如對數(shù)據(jù)庫同一個字段的讀操作和寫操作存在爭搶研乒,讀的速度過高會影響寫得速度汹忠,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源雹熬,則爭搶本身帶來的開銷會降低整體的吞吐量宽菜。

可使用關(guān)聯(lián)限流來避免具有關(guān)聯(lián)關(guān)系的資源之間過度的爭搶,舉例來說竿报,read_db 和 write_db 這兩個資源分別代表數(shù)據(jù)庫讀寫铅乡,我們可以給 read_db 設(shè)置限流規(guī)則來達到寫優(yōu)先的目的:設(shè)置 FlowRule.strategy 為 RuleConstant.STRATEGY_RELATE 同時設(shè)置 FlowRule.refResource 為 write_db。這樣當寫庫操作過于頻繁時烈菌,讀數(shù)據(jù)的請求會被限流阵幸。

3.3 根據(jù)調(diào)用鏈路入口限流:鏈路限流

RuleConstant.STRATEGY_CHAIN = 2

NodeSelectorSlot 中記錄了資源之間的調(diào)用鏈路,這些資源通過調(diào)用關(guān)系芽世,相互之間構(gòu)成一棵調(diào)用樹挚赊。這棵樹的根節(jié)點是一個名字為 machine-root 的虛擬節(jié)點,調(diào)用鏈的入口都是這個虛節(jié)點的子節(jié)點济瓢。

一棵典型的調(diào)用樹如下圖所示:

                  machine-root
                    /       \
                   /         \
             Entrance1     Entrance2
                /              \
               /                \
      DefaultNode(nodeA)   DefaultNode(nodeA)

上圖中來自入口 Entrance1 和 Entrance2 的請求都調(diào)用到了資源 NodeA荠割,Sentinel 允許只根據(jù)某個入口的統(tǒng)計信息對資源限流。

比如我們可以設(shè)置 FlowRule.strategy 為 RuleConstant.STRATEGY_CHAIN葬荷,同時設(shè)置 FlowRule.refResource 為 Entrance1 來表示只有從入口 Entrance1 的調(diào)用才會記錄到 NodeA 的限流統(tǒng)計當中涨共,而對來自 Entrance2 的調(diào)用漠不關(guān)心。

調(diào)用鏈的入口是通過 API 方法 ContextUtil.enter(name) 定義的宠漩。

四举反、源碼分析

4.1 FlowSlot

首先看FlowSlot入口類:

  @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 {
        // Flow rule map cannot be null.
        Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();

        List<FlowRule> rules = flowRules.get(resource.getName());
        if (rules != null) {
            for (FlowRule rule : rules) {
                if (!canPassCheck(rule, context, node, count, prioritized)) {
                    throw new FlowException(rule.getLimitApp());
                }
            }
        }
    }

    boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int count, boolean prioritized) {
        return FlowRuleChecker.passCheck(rule, context, node, count, prioritized);
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }

大致內(nèi)容:
1.通過FlowRuleManage獲取所有的限流規(guī)則
2.獲取該資源對應(yīng)的限流,然后循環(huán)通過canPassCheck方法判斷扒吁,若返回false則說明被限流了火鼻。

4.2 FlowRuleChecker類

 static boolean passCheck(/*@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);
    }

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

1室囊、通過selectNodeByRequesterAndStrategy方法選擇被限流的節(jié)點。
2魁索、獲取的rule的Controller調(diào)用具體的限流規(guī)則融撞。

 static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {
        // 獲取限流的limitApp,限流策略(startegy)粗蔚,上線的origin
        String limitApp = rule.getLimitApp();
        int strategy = rule.getStrategy();
        String origin = context.getOrigin();

        //如果limitApp等于origin并且origin不是default和other尝偎;
        if (limitApp.equals(origin) && filterOrigin(origin)) {
            //如果策略是STRATEGY_DIRECT(調(diào)用方限流)
            if (strategy == RuleConstant.STRATEGY_DIRECT) {
                // Matches limit origin, return origin statistic node.
                return context.getOriginNode();
            }

            return selectReferenceNode(rule, context, node);
        } else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
            if (strategy == RuleConstant.STRATEGY_DIRECT) {
                // Return the cluster node.
                return node.getClusterNode();
            }

            return selectReferenceNode(rule, context, node);
        } else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
            && FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {
            if (strategy == RuleConstant.STRATEGY_DIRECT) {
                return context.getOriginNode();
            }

            return selectReferenceNode(rule, context, node);
        }

        return null;
    }

    static Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
        String refResource = rule.getRefResource();
        int strategy = rule.getStrategy();

        if (StringUtil.isEmpty(refResource)) {
            return null;
        }

        if (strategy == RuleConstant.STRATEGY_RELATE) {
            return ClusterBuilderSlot.getClusterNode(refResource);
        }

        if (strategy == RuleConstant.STRATEGY_CHAIN) {
            if (!refResource.equals(context.getName())) {
                return null;
            }
            return node;
        }
        // No node.
        return null;
    }

1.獲取限流的limitApp,限流策略(startegy)鹏控,上線的origin致扯。
2.如果limitApp等于origin并且origin不是default和other:

如果策略是STRATEGY_DIRECT(調(diào)用方限流),則限流節(jié)點是originNode当辐;若是限流策略是STRATEGY_RELATE(關(guān)聯(lián)限流)抖僵,則限流節(jié)點是refResource的clusterNode;若是限流策略是STRATEGY_CHAIN(鏈路限流)缘揪,并且refResource等于contextName耍群,則限流節(jié)點就是node

3.如果limitApp等于default:

如果策略是STRATEGY_DIRECT(調(diào)用方限流)找筝,則限流節(jié)點是clusterNode蹈垢;若是限流策略是STRATEGY_RELATE(關(guān)聯(lián)限流),則限流節(jié)點是refResource的clusterNode呻征;若是限流策略是STRATEGY_CHAIN(鏈路限流)耘婚,并且refResource等于contextName,則限流節(jié)點就是node陆赋。

4.如果limitApp等于other并且該資源的其他限流limitApp不與origin相同:

如果策略是STRATEGY_DIRECT(調(diào)用方限流)沐祷,則限流節(jié)點是originNode;若是限流策略是STRATEGY_RELATE(關(guān)聯(lián)限流)攒岛,則限流節(jié)點是refResource的clusterNode赖临;若是限流策略是STRATEGY_CHAIN(鏈路限流),并且refResource等于contextName灾锯,則限流節(jié)點就是node兢榨。

4.3 流控規(guī)則

由rule.getRater()獲取具體的流控規(guī)則,目前有四種流控規(guī)則顺饮;直接失敗拆讯、WarmUP芯杀、排隊等候、WarmUP+排隊等候。

流控規(guī)則是在FlowRuleUtil類中設(shè)置的嘿架,根據(jù)具體的ControlBefavior進行設(shè)置甸鸟,如下代碼:


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

可以發(fā)現(xiàn)對應(yīng)關(guān)系如下:

流控類型 Controller
快速失敗 DefaultController
WarmUp WarmUpController
勻速排隊 RateLimiterController
WarmUp + 勻速排隊 WarmUpRateLimiterController
快速失敗
快速失敗

1.通過avgUsedTokens方法先獲取當前的請求線程數(shù)或者qps第队,然后加上當前請求的個數(shù)acquireCount,如果大于count則說明超過了限流控制的閾值励稳,則返回false。

Warp Up

Sentinel的WarmUp是基于Guava的算法囱井,但是不像Guava的場景驹尼,這是基于一個漏桶,主要使用基于時間間隔庞呕,
Sentinel更專注于控制計數(shù)每秒的請求而沒有計算它的間隔新翎。

Sentinel的WarmUp算法實現(xiàn)基于基于Guava的算法。然而,Guava的實現(xiàn)重點調(diào)整請求的時間間隔,換句話說,一個漏水的水桶千扶。哨兵更多關(guān)注控制計數(shù)每秒的請求沒有計算它的間隔,它更像是一個“令牌桶料祠。

剩下的令牌桶是用來測量系統(tǒng)效用。假設(shè)一個系統(tǒng)可以處理b每秒的請求澎羞。每秒鐘b標記將被添加到桶,直到桶滿了。系統(tǒng)處理一個請求時,它需要一個令牌桶敛苇。剩有令牌桶,降低系統(tǒng)的利用率;令牌桶中的令牌時超過一定閾值,我們稱之為“飽和”狀態(tài)妆绞。

基于Guava的理論,這是一個線性方程我們可以寫這個形式y = m x + y;(b.k.y(x))或每秒(q))枫攀,我們預(yù)計每秒給定一個飽和期(eg:3分鐘)括饶,m是變化的速度從我們冷(最小)率穩(wěn)定(最大),x(或q)是被占領(lǐng)的令牌来涨。

下面通過數(shù)學(xué)知識理解:

           ^ throttling
           |
  3*stable +                  /
  interval |                 /.
   (cold)  |                / .
           |               /  .   <-- "warmup period" is the area of the trapezoid between
  2*stable +              /   .       warningToken and maxToken(預(yù)熱區(qū)為這個梯形區(qū)域)
  interval |             /    .
           |            /     .
           |           /   B  .
    stable +----------/  WARM . }
  interval |          .   UP  . } <-- 這塊矩形 (寬從0至maxPermits, 高為stableInterval
           |          . PERIOD. }     定義為冷卻區(qū)域图焰,同時我們希望冷卻區(qū)==預(yù)熱區(qū)
           |    A      .      . }     cooldownPeriod == warmupPeriod
           |---------------------------------> storedPermits
              (warningToken) (maxToken)
  • storedPermits <= warningToken ,那么我用相同的速率消耗它們蹦掐,刷新permits也總是以 1/stableInterval 速率生成permits技羔。我們將這塊區(qū)域的大小定位一半的預(yù)熱區(qū)域。為什么我們需要這個卧抗?
    為啥是一半藤滥?我們將在下面簡要解釋(在解釋完第二部分之后)。
  • storedPermits一旦超過warningToken社裆,將映射到一條從stableInterva到3倍stableInterval的提升線拙绊。
    這部分的平均高度為2倍stableInterval(默認codeFactor為3),這塊區(qū)域的大小恰好等于預(yù)熱區(qū)域泳秀。
  • 在預(yù)熱區(qū)內(nèi)是獲取令牌的速度是勻速增長的标沪。
  • stableInterval為qps的時間,即1/count嗜傅。

現(xiàn)在我們看WarmUpController構(gòu)造方法:

  public WarmUpController(double count, int warmUpPeriodInSec, int coldFactor) {
        construct(count, warmUpPeriodInSec, coldFactor);
    }

    public WarmUpController(double count, int warmUpPeriodInSec) {
        construct(count, warmUpPeriodInSec, 3);
    }

    private void construct(double count, int warmUpPeriodInSec, int coldFactor) {

        if (coldFactor <= 1) {
            throw new IllegalArgumentException("Cold factor should be larger than 1");
        }

        this.count = count;

        //冷凍因子金句,默認為3
        this.coldFactor = coldFactor;

        // thresholdPermits = 0.5 * warmupPeriod / stableInterval.
        // warningToken = 100;
        warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
        // / maxPermits = thresholdPermits + 2 * warmupPeriod /
        // (stableInterval + coldInterval)
        // maxToken = 200
        maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));

        // slope
        // slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits
        // - thresholdPermits);
        slope = (coldFactor - 1.0) / count / (maxToken - warningToken);

    }
  1. warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
    warningToken為告警令牌數(shù),warmUpPeriodInSec及時B區(qū)域的面積磺陡,它是A的面積的coldFactor-1倍趴梢;stableInterval為1/count;
  2. maxToken是根據(jù)梯形的面積公式計算出來的漠畜;
  3. slope是根據(jù)斜率計算公式計算的
 @Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
        long passQps = node.passQps();

        long previousQps = node.previousPassQps();
        syncToken(previousQps);

        // 開始計算它的斜率
        // 如果進入了警戒線,開始調(diào)整他的qps
        long restToken = storedTokens.get();
        if (restToken >= warningToken) {
            long aboveToken = restToken - warningToken;
            // 消耗的速度要比warning快坞靶,但是要比慢
            // current interval = restToken*slope+1/count
            double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));
            if (passQps + acquireCount <= warningQps) {
                return true;
            }
        } else {
            if (passQps + acquireCount <= count) {
                return true;
            }
        }

        return false;
    }
  1. 如果restToken進入了警戒線憔狞,開始調(diào)整他的qps,根據(jù)斜率計算出warningQps彰阴;若passQps + acquireCount小于warningQps則請求通過瘾敢。
  2. 如果沒有進入警戒線,若passQps + acquireCount <= count則請求通過尿这。
  3. 若1和2不滿足簇抵,則請求不通過。
勻速排隊
 @Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
        long currentTime = TimeUtil.currentTimeMillis();
        // Calculate the interval between every two requests.
        long costTime = Math.round(1.0 * (acquireCount) / count * 1000);

        // Expected pass time of this request.
        long expectedTime = costTime + latestPassedTime.get();

        if (expectedTime <= currentTime) {
            // Contention may exist here, but it's okay.
            latestPassedTime.set(currentTime);
            return true;
        } else {
            // Calculate the time to wait.
            long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();
            if (waitTime >= maxQueueingTimeMs) {
                return false;
            } else {
                long oldTime = latestPassedTime.addAndGet(costTime);
                try {
                    waitTime = oldTime - TimeUtil.currentTimeMillis();
                    if (waitTime >= maxQueueingTimeMs) {
                        latestPassedTime.addAndGet(-costTime);
                        return false;
                    }
                    Thread.sleep(waitTime);
                    return true;
                } catch (InterruptedException e) {
                }
            }
        }
        return false;
    }

1.根據(jù)qps計算兩次請求的時間間隔并且獲取當前請求的期待時間射众。
2.如果期待時間小于當前時間碟摆,則請求通過;否則先獲取waitTime叨橱,若waitTime大于maxQueueingTimeMs隊列排隊時間典蜕,則請求阻止。
3.通過latestPassedTime.addAndGet(costTime)加上costTime;若此時waitTime還大于maxQueueingTimeMs隊列排隊時間罗洗,latestPassedTime時間恢復(fù)加costTime之前的值愉舔,并請求阻止;否則線程睡眠waitTime時間伙菜,并請求通過轩缤。

WarmUp + 勻速排隊

這種選擇就是WarmUp與勻速排隊組合,具體可見源碼贩绕。

五火的、我的總結(jié)

1、介紹了Sentinel的限流規(guī)則以及限流原理丧叽。
2卫玖、FlowSlot是整個插槽鏈中最復(fù)雜的一塊,主要根據(jù)了前面 NodeSelectorSlot踊淳、ClusterNodeBuilderSlot假瞬、StatistcSlot 統(tǒng)計出來的實時信息進行流量控制。
3迂尝、閾值類型有兩種(限流閾值類型脱茉,QPS 或線程數(shù)),流控模式有三種(直接垄开、關(guān)聯(lián)琴许、鏈路),流控效果有四種(快速失敗溉躲、WarmUP榜田、排隊等候益兄、WarmUP+排隊等候)。
4箭券、WarmUP限流是根據(jù)Guava的令牌桶算法演變而來的净捅。


以上內(nèi)容,若有不當之處辩块,請指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛔六,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子废亭,更是在濱河造成了極大的恐慌国章,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豆村,死亡現(xiàn)場離奇詭異液兽,居然都是意外死亡,警方通過查閱死者的電腦和手機你画,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門抵碟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坏匪,你說我怎么就攤上這事∏送常” “怎么了适滓?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恋追。 經(jīng)常有香客問我凭迹,道長,這世上最難降的妖魔是什么苦囱? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任嗅绸,我火速辦了婚禮,結(jié)果婚禮上撕彤,老公的妹妹穿的比我還像新娘鱼鸠。我一直安慰自己,他們只是感情好羹铅,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布蚀狰。 她就那樣靜靜地躺著,像睡著了一般职员。 火紅的嫁衣襯著肌膚如雪麻蹋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天焊切,我揣著相機與錄音扮授,去河邊找鬼芳室。 笑死,一個胖子當著我的面吹牛刹勃,可吹牛的內(nèi)容都是我干的堪侯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼深夯,長吁一口氣:“原來是場噩夢啊……” “哼抖格!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咕晋,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤雹拄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后掌呜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滓玖,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年质蕉,在試婚紗的時候發(fā)現(xiàn)自己被綠了势篡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡模暗,死狀恐怖禁悠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兑宇,我是刑警寧澤碍侦,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站隶糕,受9級特大地震影響瓷产,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枚驻,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一濒旦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧再登,春花似錦尔邓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沈撞,卻和暖如春慷荔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工显晶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贷岸,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓磷雇,卻偏偏與公主長得像偿警,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唯笙,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一螟蒸、概述 前面介紹過Sentinel核心框架就是通過插槽鏈一層層的調(diào)用,每個插槽的功能如下: NodeSelect...
    橘子_好多灰閱讀 1,529評論 0 4
  • 投射孩子平安健康快樂崩掘,與宇宙正能量同頻共振七嫌,無限潛能被激發(fā),自主愉悅苞慢,自主健康快樂… 投射孩子今天與老師同頻共振诵原,...
    zm_3067閱讀 155評論 0 0
  • 01 我跟舍友不是一個研究方向,自然不在一個實驗室挽放。每個實驗室又有每個實驗室的特點绍赛。相比她的實驗室來說,我的實驗室...
    琛以沫閱讀 295評論 0 1
  • 關(guān)于宗岱譯集首發(fā)式的發(fā)言(提綱) 學(xué)理上沒有研究 生平事跡方面沒有新的發(fā)現(xiàn) 贊賞:何……辑畦,出版社吗蚌;劉盧二位的熱心…...
    黃葉子閱讀 411評論 0 0