令牌桶、漏斗、冷啓動限流在sentinel的應用

 分佈式系統爲了保證系統穩定性,在服務治理的限流中會根據不同場景進行限流操作,常見的限流算法有:

  • 令牌桶:可容忍一定突發流量的速率的限流,令牌桶算法的原理是系統以恆定的速率產生令牌,然後把令牌放到令牌桶中,令牌桶有一個容量,當令牌桶滿了的時候,再向其中放令牌,那麼多餘的令牌會被丟棄;當想要處理一個請求的時候,需要從令牌桶中取出一個令牌,如果此時令牌桶中沒有令牌,那麼則拒絕該請求。

  • 漏斗:固定速率限流,可以啓動整流作用。

在分析sentinel限流之前,我們先看下sentinel是什麼,官網說明如下:

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分佈式服務架構的流量控制組件,主要以流量爲切入點,從流量控制、熔斷降級、系統自適應保護等多個維度來幫助您保障微服務的穩定性。

從限流角度來看,sentinel的限流有2種控制維度,一個是qps,一個是併發數。

qps這個很好理解,也就是每秒處理請求量,當超過設定閾值時,會進行流控,策略有如下幾種:拒絕、排隊(一定時長)等。

併發數這個就是當前線程運行數,類似於hystrix,只不過sentinel是進行線程個數統計判斷是否達到線程設定值,而hystrix是根據不同線程池來做的。

sentinel中處理流程是一個責任鏈,不同功能的邏輯抽象成不同的ProcessorSlot組合在一起,比如有限流的FlowSlot、打日誌的LogSot、數據統計的StatisticSlot。下面重點看限流的com.alibaba.csp.sentinel.slots.block.flow.FlowSlot

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

public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
                      Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
    Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
    for (FlowRule rule : rules) { // 多個限流規則檢查
        if (!canPassCheck(rule, context, node, count, prioritized)) {
            throw new FlowException(rule.getLimitApp(), rule);
        }
    }
}
// canPassCheck -> passLocalCheck
private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                      boolean prioritized) {
    return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}

canPass校驗目前有以下幾種實現類:

這幾個實現類分別使用瞭如下幾種限流算法:

  • DefaultController:令牌桶

  • RateLimiterController:漏斗

  • WarmUpController:冷啓動的令牌桶

  • WarmUpRateLimiterController:冷啓動的漏斗

sentinel中統計信息,比如qps、pass、block等信息都是在滑動時間窗口中維護的,比如時間戳是910時,統計信息會往對應800-1000的時間窗口更新,當時間戳是1001時,由於時間窗口只有5個(每個200ms),因此會複用第一個時間窗口,在使用前會先進行初始化該窗口統計值。

對於默認的流控實現 DefaultController,其是根據時間窗口的統計值是否達到了限流值來決定是否限流的,這也是把它歸爲令牌桶算法的原因。漏斗算法實現RateLimiterController,會記錄上一次正常通過的時間戳信息(latestPassedTime),當判斷是否限流時,會根據當前時間-latestPassedTime是否大於間隔值,大於的話表示可以正常通過,小於的話表示剛剛已經有流程正常通過,此次需要排隊等待,等待時間爲期望時間戳-當前時間戳,併發場景下,多個線程可能都會走到等待這裏,因此需要(cas操作)判斷當前需等待時間是否大於某個值,大於的話直接進行限流,不再排隊等待。

冷啓動限流算法,即預熱/冷啓動方式。當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉昇到高水位可能瞬間把系統壓垮。通過"冷啓動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。

sentinel中通常冷啓動的過程系統允許通過的 QPS 曲線如下圖所示:

冷啓動的兩種模式,令牌桶和漏斗大同小異,只不過在流量較大時,冷啓動過程 令牌桶走勢類似於階梯向上直到設定的限流值,漏洞走勢類似於幾個斜線向上之道設定的限流值。

關於sentinel更多的知識可參考官方文檔:https://sentinelguard.io/zh-cn/docs/introduction.html

 

 推薦閱讀 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章