一余素、冷啟動含義
除了勻速器奔穿,另一種在面對RocketMQ 場景下流量突增時來保障系統(tǒng)穩(wěn)定性的的方式是冷啟動镜沽。Sentinel的Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預熱/冷啟動方式巫橄。當系統(tǒng)長期處于低水位的情況下淘邻,當流量突然增加時,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮湘换。通過"冷啟動"宾舅,讓通過的流量緩慢增加,在一定時間內(nèi)逐漸增加到閾值上限彩倚,給冷系統(tǒng)一個預熱的時間筹我,避免冷系統(tǒng)被壓垮。warm up冷啟動主要用于啟動需要額外開銷的場景帆离,例如建立數(shù)據(jù)庫連接等蔬蕊。
二、驗證實例
1哥谷、首先設(shè)置限流規(guī)則以及流控效果(采用warm up冷啟動方式)
private static void initFlowRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(KEY);
// 設(shè)置最大閾值為20
// rule1.setCount(20);
// 這里設(shè)置QPS最大的閾值1000, 便于查看變化曲線
rule1.setCount(1000);
// 基于QPS流控規(guī)則
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 默認不區(qū)分調(diào)用來源
rule1.setLimitApp("default");
// 流控效果, 采用warm up冷啟動方式
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
// 在一定時間內(nèi)逐漸增加到閾值上限岸夯,給冷系統(tǒng)一個預熱的時間,避免冷系統(tǒng)被壓垮们妥。
// warmUpPeriodSec 代表期待系統(tǒng)進入穩(wěn)定狀態(tài)的時間(即預熱時長)猜扮。
// 這里預熱時間為1min, 便于在dashboard控制臺實時監(jiān)控查看QPS的pass和block變化曲線
rule1.setWarmUpPeriodSec(60); // 默認值為10s
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
2、啟動一個TimerTask線程, 統(tǒng)計每一秒的pass, block, total這三個指標
static class TimerTask implements Runnable {
@Override
public void run() {
long start = System.currentTimeMillis();
System.out.println("begin to statistic!!!");
long oldTotal = 0;
long oldPass = 0;
long oldBlock = 0;
while (!stop) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
long globalTotal = total.get();
long oneSecondTotal = globalTotal - oldTotal;
oldTotal = globalTotal;
long globalPass = pass.get();
long oneSecondPass = globalPass - oldPass;
oldPass = globalPass;
long globalBlock = block.get();
long oneSecondBlock = globalBlock - oldBlock;
oldBlock = globalBlock;
System.out.println("currentTimeMillis:" + TimeUtil.currentTimeMillis() + ", totalSeconds:"
+ TimeUtil.currentTimeMillis() / 1000 + ", currentSecond:"
+ (TimeUtil.currentTimeMillis() / 1000) % 60 + ", total:" + oneSecondTotal
+ ", pass:" + oneSecondPass + ", block:" + oneSecondBlock);
if (seconds-- <= 0) {
stop = true;
}
}
long cost = System.currentTimeMillis() - start;
System.out.println("time cost: " + cost + " ms");
System.out.println("total:" + total.get() + ", pass:" + pass.get() + ", block:" + block.get());
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
}
pass:是限流允許的訪問數(shù)量监婶,block:是被限流的訪問數(shù)量旅赢,total是總數(shù)量。
3惑惶、同時啟動三個WarmUpTask線程, 設(shè)置其休眠時間小于2s, 使系統(tǒng)訪問資源處于一個較低的流量
// 創(chuàng)建3個WarmUpTask線程, 模擬一個系統(tǒng)處于一個低水平流量
for (int i = 0; i < 3; i++) {
Thread t = new Thread(new WarmUpTask());
t.setName("sentinel-warmup-task");
t.start();
}
//WarmUpTask線程休眠小于2s, 通過控制休眠時間, 達到控制訪問資源的流量處于一個較低的水平.
static class WarmUpTask implements Runnable {
@Override
public void run() {
while (!stop) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
// token acquired, means pass
pass.addAndGet(1);
} catch (BlockException e1) {
block.incrementAndGet();
} catch (Exception e2) {
// biz exception
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
Random random2 = new Random();
try {
// 隨機休眠時間<2s, 通過設(shè)置休眠時間(擋板業(yè)務(wù)耗時), 模擬訪問資源的流量大小
TimeUnit.MILLISECONDS.sleep(random2.nextInt(2000));
} catch (InterruptedException e) {
// ignore
}
}
}
}
4煮盼、WarmUpTask線程運行20s后,再同時啟動100個線程, 設(shè)置其休眠時間小于50ms, 這樣就模擬造成了訪問資源的流量突增, 一是可以查看后臺console觀察流量變化數(shù)值, 二是查看監(jiān)控臺的實時監(jiān)控, 能比較直觀的看見warm up過程.
// 20s開始有突增的流量進來, 訪問資源
Thread.sleep(20000);
// 創(chuàng)建一個100線程, 模擬突增的流量訪問被保護的資源
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread(new RunTask());
t.setName("sentinel-run-task");
t.start();
}
static class RunTask implements Runnable {
@Override
public void run() {
while (!stop) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
pass.addAndGet(1);
} catch (BlockException e1) {
//以原子方式將當前值加 1。
block.incrementAndGet();
} catch (Exception e2) {
// biz exception
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
Random random2 = new Random();
try {
// 隨機休眠時間<50ms, 通過設(shè)置休眠時間, 模擬訪問資源的流量大小
TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
} catch (InterruptedException e) {
// ignore
}
}
}
}
5带污、控制臺中打印的日志的展示效果是:
可以看到在20秒時有一個明顯的流量激增僵控,total由原來的一百多突然長到三千多pass也激增到幾百了,block也長到了幾千鱼冀。
接著往下看
可以看到由于我們設(shè)置的閾值為1000, 所以最終的pass值是穩(wěn)定在1000沒有問題; 流控效果采用warm up方式, pass的值不是一下子增加到1000, 而是由300-->400-->500-->600-->700-->800-->900-->1000逐漸增加的报破。最終QPS流量穩(wěn)定在最大閾值1000。
6雷绢、sentinel控制臺展示效果圖:
剛開始在qps未激增的時候:
激增之后有一個明顯的激增點泛烙,也可以看到通過的QPS也就是圖中紅色的線理卑,是平穩(wěn)上升的:
由于闊值是1000可以看到經(jīng)過60s的預熱最后穩(wěn)定到1000
7翘紊、總結(jié)
上面主要講述了QPS流量控制, 采用Warm Up預熱/冷啟動方式控制突增流量, 通過在后臺console觀察打印的數(shù)據(jù)以及結(jié)合dashboard圖表的形式, 能很清晰的了解到warm up冷啟動方式控制突增流量, 保護資源, 維護系統(tǒng)的穩(wěn)定性的.