使用Sentinel滑動(dòng)窗口實(shí)現(xiàn)限流和降級(jí)
Sentinel滑動(dòng)窗口前言
Sentinel 是一個(gè)開(kāi)源的高可用性、高擴(kuò)展性的實(shí)時(shí)流量控制框架,它可以用于保護(hù)服務(wù)穩(wěn)定性,防止系統(tǒng)因?yàn)榱髁窟^(guò)大而崩潰。今天我們所介紹的,是滑動(dòng)窗口,它是 Sentinel 實(shí)現(xiàn)限流和降級(jí)的重要組件之一。
Sentinel滑動(dòng)窗口原理
滑動(dòng)窗口通常由一個(gè)固定大小的時(shí)間窗口和一個(gè)可滑動(dòng)的數(shù)據(jù)結(jié)構(gòu)組成,例如隊(duì)列或滑動(dòng)塊。在 Sentinel 中,滑動(dòng)窗口被分為計(jì)數(shù)器類型和預(yù)算類型:
計(jì)數(shù)器滑動(dòng)窗口:計(jì)數(shù)器滑動(dòng)窗口維護(hù)一個(gè)周期內(nèi)請(qǐng)求的總數(shù),當(dāng)周期結(jié)束后,滑動(dòng)窗口會(huì)被清空,開(kāi)始下一個(gè)周期的計(jì)數(shù)。在 Sentinel 中,計(jì)數(shù)器滑動(dòng)窗口通過(guò) AtomicLong 數(shù)組實(shí)現(xiàn),數(shù)組的長(zhǎng)度代表時(shí)間窗口的大小,每個(gè)元素代表該時(shí)間片保存的請(qǐng)求次數(shù)。
預(yù)算滑動(dòng)窗口:預(yù)算滑動(dòng)窗口維護(hù)周期內(nèi)預(yù)算資源的使用情況,以便根據(jù)實(shí)際資源需要進(jìn)行限流或降級(jí)。預(yù)算類型的滑動(dòng)窗口可以設(shè)置閾值,并支持動(dòng)態(tài)調(diào)整。在 Sentinel 中,預(yù)算滑動(dòng)窗口通常用于 CPU 使用率、線程池等容量資源的控制。
除此之外,Sentinel 還提供了基于單機(jī)、集群和分布式三種模式的滑動(dòng)窗口實(shí)現(xiàn)。其中,單機(jī)模式下將使用本地內(nèi)存來(lái)維護(hù)滑動(dòng)窗口數(shù)據(jù)結(jié)構(gòu);集群模式下,各節(jié)點(diǎn)會(huì)通過(guò)多播或 Redis 等方式共享相同的計(jì)數(shù)器數(shù)據(jù);分布式模式下,則允許多個(gè)服務(wù)節(jié)點(diǎn)公用同一組滑動(dòng)窗口數(shù)據(jù)。
在使用 Sentinel 進(jìn)行限流和降級(jí)時(shí),用戶可以通過(guò)配置規(guī)則來(lái)指定需要監(jiān)控的目標(biāo)資源,并設(shè)置相應(yīng)的閾值和處理策略。例如,在限流場(chǎng)景下,用戶可以設(shè)置最大 QPS,并指定超過(guò)該閾值的請(qǐng)求將會(huì)被丟棄或返回特定的錯(cuò)誤碼。在降級(jí)場(chǎng)景下,用戶可以設(shè)置響應(yīng)時(shí)間、異常比例等閾值,并指定相應(yīng)的降級(jí)策略,例如返回默認(rèn)值或者直接拋出異常。
如何在項(xiàng)目中使用滑動(dòng)窗口
以實(shí)際的項(xiàng)目為例子,我們有一個(gè)服務(wù)用于提供用戶信息查詢接口,每秒鐘最多只能處理 10 次查詢請(qǐng)求,并且如果連續(xù) 5 秒內(nèi)的請(qǐng)求次數(shù)超過(guò) 50 次,則觸發(fā)限流或降級(jí)操作。
在 Sentinel 中,我們首先需要定義一個(gè) FlowRule 數(shù)組用于配置針對(duì)該服務(wù)的限流規(guī)則,在這塊項(xiàng)目例子中,它們是每秒鐘限流到 10 個(gè)請(qǐng)求和 5 秒內(nèi)超過(guò) 50 次請(qǐng)求兩種限制:
FlowRule rule1 = new FlowRule();
rule1.setResource("queryUser");
rule1.setCount(10);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
FlowRule rule2 = new FlowRule();
rule2.setResource("queryUser");
rule2.setCount(50);
rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule2.setStrategy(RuleConstant.STRATEGY_CHAIN);
rule2.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);我們先對(duì)以上代碼進(jìn)行解釋,“queryUser” 代表資源名稱,這個(gè)可以自己定義,然后設(shè)置了resource、count、grade以及對(duì)于第二個(gè)規(guī)則還增加了三個(gè)屬性stratety, controlBehavior和controlBehavior。其中 count 表示限流閾值,即每秒鐘最多可以處理多少個(gè)請(qǐng)求;grade 表示限流模式,QPS(每秒鐘最多允許通過(guò)的請(qǐng)求數(shù)量);strategy 表示限流策略;controlBehavior 表示流量控制的行為。
接下來(lái),我們需要通過(guò)執(zhí)行如下代碼初始化 Sentinel:
List<FlowRule> rules = new ArrayList<>(); rules.add(rule1); rules.add(rule2); FlowRuleManager.loadRules(rules);
我們這里定義了一個(gè) FlowRule 數(shù)組,將前面定義好的兩個(gè)規(guī)則加入到列表中,然后通過(guò) FlowRuleManager.loadRules() 方法加載這些規(guī)則。
現(xiàn)在,我們可以在查詢用戶信息的方法上增加 @SentinelResource 注解,并在該注解中設(shè)置 blockHandler 和 fallback 屬性來(lái)處理限流和降級(jí)異常:
@SentinelResource(value = "queryUser", blockHandler = "queryUserBlockHandler",
fallback = "queryUserFallback")
public UserInfo queryUser(String userId) {
// 查找用戶信息的實(shí)現(xiàn)
}
// 限流/降級(jí)處理函數(shù)
public UserInfo queryUserBlockHandler(String userId, BlockException ex) {
// 返回具體的錯(cuò)誤信息
}
// 降級(jí)處理
public UserInfo queryUserFallback(String userId, Throwable ex) {
// 返回降級(jí)后的響應(yīng)
}以上代碼中我們定義了一個(gè)名為“queryUser”的資源,它被 @SentinelResource 注解所修飾,blockHandler 屬性表示當(dāng)該服務(wù)觸發(fā)限流或降級(jí)時(shí)調(diào)用的函數(shù),fallback 屬性表示服務(wù)出現(xiàn)異常時(shí)的回退函數(shù)。
最后,我們可以通過(guò)對(duì)定時(shí)任務(wù)進(jìn)行調(diào)度,模擬10秒內(nèi)對(duì)同一個(gè) API 請(qǐng)求用戶數(shù)據(jù)的場(chǎng)景,例如代碼如下:
public class QueryUserTask implements Runnable {
private static final Random random = new Random();
@Override
public void run() {
// 模擬 10 秒內(nèi)對(duì)同一個(gè) API 請(qǐng)求用戶數(shù)據(jù)
for (int i = 0; i < 20; i++) {
String userId = "user-" + (i % 5);
queryUser(userId);
try {
Thread.sleep(random.nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}我們可以在 QueryUserTask 內(nèi)部實(shí)現(xiàn)每隔一秒獲取一次用戶信息,檢測(cè)系統(tǒng)是否觸發(fā)限流或降級(jí)保護(hù)。如果觸發(fā)了規(guī)則中配置的限流規(guī)則,那么 Sentinel 將執(zhí)行相應(yīng)的措施進(jìn)行處理。
好了,本篇文章就先分享到這里了,后續(xù)將會(huì)繼續(xù)介紹sentinel詳細(xì)的其他方面的知識(shí)。
到此這篇關(guān)于使用Sentinel滑動(dòng)窗口實(shí)現(xiàn)限流和降級(jí)的文章就介紹到這了,更多相關(guān)Sentinel限流和降級(jí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis中一對(duì)多(collection)和一對(duì)一(association)的組合查詢使用
這篇文章主要介紹了Mybatis中一對(duì)多(collection)和一對(duì)一(association)的組合查詢使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
MyBatis XML去除多余AND|OR前綴或逗號(hào)等后綴的操作
這篇文章主要介紹了MyBatis XML去除多余AND|OR前綴或逗號(hào)等后綴的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Java非靜態(tài)成員變量之死循環(huán)(詳解)
下面小編就為大家?guī)?lái)一篇Java非靜態(tài)成員變量之死循環(huán)(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
RabbitMQ死信機(jī)制實(shí)現(xiàn)延遲隊(duì)列的實(shí)戰(zhàn)
本文主要介紹了RabbitMQ死信機(jī)制實(shí)現(xiàn)延遲隊(duì)列的實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
SpringBoot熔斷機(jī)制之CircuitBreaker詳解
這篇文章主要介紹了SpringBoot熔斷機(jī)制之CircuitBreaker詳解,SpringBoot的熔斷機(jī)制在微服務(wù)架構(gòu)中扮演著重要角色,其中CircuitBreaker是其核心機(jī)制之一,用于防止服務(wù)的異常狀態(tài)影響到整個(gè)系統(tǒng)的運(yùn)作,需要的朋友可以參考下2023-10-10
淺談spring和spring MVC的區(qū)別與關(guān)系
下面小編就為大家?guī)?lái)一篇淺談spring和spring MVC的區(qū)別與關(guān)系。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
基于springMvc+hibernate的web application的構(gòu)建
下面小編就為大家?guī)?lái)一篇基于springMvc+hibernate的web application的構(gòu)建。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10

