一肝断、什么是削峰填谷:
某瞬時來了大流量的請求, 而如果此時要處理所有請求杈曲,很可能會導(dǎo)致系統(tǒng)負(fù)載過高,影響穩(wěn)定性胸懈。但其實(shí)可能后面幾秒之內(nèi)都沒有消息投遞鱼蝉,若直接把多余的消息丟掉則沒有充分利用系統(tǒng)處理消息的能力。Sentinel的Rate Limiter模式能在某一段時間間隔內(nèi)以勻速方式處理這樣的請求, 充分利用系統(tǒng)的處理能力, 也就是削峰填谷, 保證資源的穩(wěn)定性.
Sentinel會以固定的間隔時間讓請求通過, 訪問資源箫荡。當(dāng)請求到來的時候,如果當(dāng)前請求距離上個通過的請求通過的時間間隔不小于預(yù)設(shè)值渔隶,則讓當(dāng)前請求通過羔挡;否則洁奈,計(jì)算當(dāng)前請求的預(yù)期通過時間,如果該請求的預(yù)期通過時間小于規(guī)則預(yù)設(shè)的 timeout 時間绞灼,則該請求會等待直到預(yù)設(shè)時間到來通過利术;反之,則馬上拋出阻塞異常低矮。
使用Sentinel的這種策略, 簡單點(diǎn)說, 就是使用一個時間段(比如20s的時間)處理某一瞬時產(chǎn)生的大量請求, 起到一個削峰填谷的作用, 從而充分利用系統(tǒng)的處理能力, 下圖能很形象的展示這種場景: X軸代表時間, Y軸代表系統(tǒng)處理的請求.
二印叁、驗(yàn)證
模擬1000個請求同時并發(fā)的訪問資源, 這1000個請求, 如果設(shè)置QPS閾值為100, 按照默認(rèn)sentinel默認(rèn)的RuleConstant.CONTROL_BEHAVIOR_DEFAULT拒絕策略, 那么剩余的900個請求會被立即直接拒絕掉, 拋出BlockException.
同樣是模擬1000個請求同時并發(fā)訪問資源, 同樣設(shè)置QPS閾值100, 但是拒絕策略修改為Rate Limiter勻速RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER方式, 還需要設(shè)置setMaxQueueingTimeMs(20 * 1000)表示每一請求最長等待時間, 這里等待時間大一點(diǎn), 以保證讓所有請求都能正常通過。
假設(shè)這里設(shè)置的排隊(duì)等待時間過小的話, 導(dǎo)致排隊(duì)等待的請求超時而拋出異常BlockException, 最終結(jié)果可能是這1000個并發(fā)請求中只有幾個請求或幾十個才能正常通過, 所以使用這種模式得根據(jù)訪問資源的耗時時間決定排隊(duì)等待時間军掂。
按照目前這種設(shè)置, QPS閾值為100的話, 每一個請求相當(dāng)于是以勻速10ms左右通過轮蜕。
@GetMapping("/paceFlow")
public void paceFlow() throws InterruptedException{
System.out.println("pace behavior");
countDown = new CountDownLatch(1);
// queuing隊(duì)列方式, 勻速處理流量
initPaceFlowRule();
// 直接并發(fā)同時啟動1000個線程, 模擬1000個并發(fā)請求資源
simulatePulseFlow();
countDown.await();
System.out.println("done");
System.out.println("total pass:" + pass.get() + ", total block:" + block.get());
System.out.println();
}
勻速模式的規(guī)則
private static void initPaceFlowRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(KEY);
rule1.setCount(count);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
/*
* CONTROL_BEHAVIOR_RATE_LIMITER means requests more than threshold will be queueing in the queue, until the
* queueing time is more than {@link FlowRule#maxQueueingTimeMs}, the requests will be rejected.
*/
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
// 這里設(shè)置的等待處理時間較大, 讓系統(tǒng)能平穩(wěn)的處理所有的請求
rule1.setMaxQueueingTimeMs(20 * 1000);// 表示每一個請求的最長等待時間20s
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
并發(fā)啟動1000線程模擬請求
private static void simulatePulseFlow() {
for (int i = 0; i < requestQps; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
long startTime = TimeUtil.currentTimeMillis();
Entry entry = null;
try {
entry = SphU.entry(KEY);
pass.incrementAndGet();
} catch (BlockException e1) {
System.out.println("===>BlockException");
block.incrementAndGet();
} catch (Exception e2) {
// biz exception
} finally {
if (entry != null) {
entry.exit();
// pass.incrementAndGet();
long cost = TimeUtil.currentTimeMillis() - startTime;
System.out.println(TimeUtil.currentTimeMillis() + " one request pass, cost " + cost
+ " ms");
}
}
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException e1) {
// ignore
}
if (done.incrementAndGet() >= requestQps) {
countDown.countDown();
}
}
}, "Thread " + i);
thread.start();
}
}
啟動后控制臺打印:
可以看到pass:1000蝗锥,block:0跃洛,可以看到在闊值設(shè)定為100,勻速模式下仍可以全部通過终议,實(shí)現(xiàn)了削峰填谷汇竭。
sentinel控制臺展示效果圖:
可以看到由于闊值是100,而模擬請求是1000穴张,通過的qps就是100细燎,勻速通過直到全部pass。
還可以在控制臺看到代碼所限定的流控規(guī)則: