Sentinel基本使用--基於QPS流量控制(二), 采用Warm Up預熱/冷啟動方式控制突增流量
一, Warm Up
Sentinel的Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即預熱/冷啟動方式。當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。warm up冷啟動主要用於啟動需要額外開銷的場景,例如建立數據庫連接等。
二, 實例
本文結合sentinel提供的示例, 通過dashboard控制台展示warm up方式啟動流量曲線變化,
WarmUpFlowDemo類說明:
1 初始化基於QPS流控規則, 流控效果使用warm up; 閾值 : 1000, 預熱時間60s;
-
private static void initFlowRule() {
-
List<FlowRule> rules = new ArrayList<FlowRule>();
-
FlowRule rule1 = new FlowRule();
-
rule1.setResource(KEY);
-
// 這里設置QPS最大的閾值1000, 盡量設置大一點, 便於在監控台查看流量變化曲線
-
rule1.setCount( 1000);
-
// 基於QPS流控規則
-
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
-
// 默認不區分調用來源
-
rule1.setLimitApp( "default");
-
// 流控效果, 采用warm up冷啟動方式
-
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
-
// 在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。
-
// warmUpPeriodSec 代表期待系統進入穩定狀態的時間(即預熱時長)。
-
// 這里預熱時間為1min, 便於在dashboard控制台實時監控查看QPS的pass和block變化曲線
-
rule1.setWarmUpPeriodSec( 60); // 默認值為10s
-
-
rules. add(rule1);
-
FlowRuleManager.loadRules(rules);
-
}
2 啟動一個TimerTask線程, 統計每一秒的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());
-
System.exit( 0);
-
}
-
}
3 同時啟動三個WarmUpTask線程, 設置其休眠時間小於2s, 使系統訪問資源處於一個較低的流量 .
①同時啟動3個WarmUpTask線程
-
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 {
-
-
-
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, 通過設置休眠時間, 模擬訪問資源的流量大小
-
TimeUnit.MILLISECONDS.sleep(random2.nextInt( 2000));
-
} catch (InterruptedException e) {
-
// ignore
-
}
-
}
-
}
-
}
4 WarmUpTask線程運行20s后,再同時啟動100個線程, 設置其休眠時間小於50ms, 這樣就模擬造成了訪問資源的流量突增, 一是可以查看后台console觀察流量變化數值, 而是查看監控台的實時監控, 能比較直觀的看見warm up過程.
①20s后, 再同時啟動100個線程
-
// 20s開始有突增的流量進來, 訪問資源
-
Thread.sleep( 20000);
②再同時啟動100個線程, 模擬突增的流量訪問資源
-
// 創建一個100線程, 模擬突增的流量訪問被保護的資源
-
for (int i = 0; i < threadCount; i++) {
-
Thread t = new Thread(new RunTask());
-
t.setName( "sentinel-run-task");
-
t.start();
-
}
③RunTask線程休眠時間小於50ms, 這樣每個線程就能多次的訪問資源, 模擬造成資源被突增的流量訪問. 這樣對資源的訪問流量就處於一個較高的水平.
-
static class RunTask implements Runnable {
-
-
-
public void run() {
-
while (!stop) {
-
Entry entry = null;
-
try {
-
entry = SphU.entry(KEY);
-
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 {
-
// 隨機休眠時間<50ms, 通過設置休眠時間, 模擬訪問資源的流量大小
-
TimeUnit.MILLISECONDS.sleep(random2.nextInt( 50));
-
} catch (InterruptedException e) {
-
// ignore
-
}
-
}
-
}
-
}
三, 后台console端每秒展示pass, block, total數據.
①從下圖可以很明顯的看出, 有一個很明顯的流量激增, total由原來的幾或者幾十, 突然增加到了4000左右, 而pass也是陡然的增加到了幾百, block也由原來的0變成了3500左右.
②接着往下看, 由於我們設置的閾值為1000, 所以最終的pass值是穩定在1000沒有問題; 流控效果采用warm up方式, pass的值不是一下子增加到1000, 而是由300-->400-->500-->600-->700-->800-->900-->1000逐漸增加的.
③最終QPS流量穩定在最大閾值1000, 如下圖:
四, dashboard控制台流量曲線展示
① 下圖展示的是, 訪問資源的流量剛開始處於一個較低的水平, QPS大概只有3左右;
②下圖可以明顯的看到綠曲線p_qps是一個逐漸上升的過程, 代表着訪問資源的流量逐漸變大, 最終穩定在閾值1000QPS.
③下圖, 展示的是38分51秒左右, 經過60s的預熱, QPS最終達到閾值1000.
完整代碼:
-
public class WarmUpFlowDemo {
-
-
private static final String KEY = "abc";
-
-
private static AtomicInteger pass = new AtomicInteger();
-
private static AtomicInteger block = new AtomicInteger();
-
private static AtomicInteger total = new AtomicInteger();
-
-
private static volatile boolean stop = false;
-
-
private static final int threadCount = 100;
-
private static int seconds = 60 + 40;
-
-
public static void main(String[] args) throws Exception {
-
initFlowRule();
-
// trigger Sentinel internal init
-
Entry entry = null;
-
try {
-
entry = SphU.entry(KEY);
-
} catch (Exception e) {
-
} finally {
-
if (entry != null) {
-
entry.exit();
-
}
-
}
-
-
Thread timer = new Thread(new TimerTask());
-
timer.setName( "sentinel-timer-task");
-
timer.start();
-
-
// first make the system run on a very low condition
-
// 創建3個線程, 模擬一個系統處於一個低水平流量
-
for (int i = 0; i < 3; i++) {
-
Thread t = new Thread(new WarmUpTask());
-
t.setName( "sentinel-warmup-task");
-
t.start();
-
}
-
-
// 20s開始有突增的流量進來, 訪問資源
-
Thread.sleep( 20000);
-
-
/*
-
* Start more thread to simulate more qps. Since we use {@link RuleConstant.CONTROL_BEHAVIOR_WARM_UP} as {@link
-
* FlowRule#controlBehavior}, real passed qps will increase to {@link FlowRule#count} in {@link
-
* FlowRule#warmUpPeriodSec} seconds.
-
*/
-
// 創建一個100線程, 模擬突增的流量訪問被保護的資源
-
for (int i = 0; i < threadCount; i++) {
-
Thread t = new Thread(new RunTask());
-
t.setName( "sentinel-run-task");
-
t.start();
-
}
-
}
-
-
private static void initFlowRule() {
-
List<FlowRule> rules = new ArrayList<FlowRule>();
-
FlowRule rule1 = new FlowRule();
-
rule1.setResource(KEY);
-
// 設置最大閾值為20
-
// rule1.setCount(20);
-
// 這里設置QPS最大的閾值1000, 便於查看變化曲線
-
rule1.setCount( 1000);
-
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
-
rule1.setLimitApp( "default");
-
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
-
// 在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。
-
// warmUpPeriodSec 代表期待系統進入穩定狀態的時間(即預熱時長)。
-
// 這里預熱時間為1min, 便於在dashboard控制台實時監控查看QPS的pass和block變化曲線
-
rule1.setWarmUpPeriodSec( 60); // 默認值為10s
-
-
rules.add(rule1);
-
FlowRuleManager.loadRules(rules);
-
}
-
-
static class WarmUpTask implements Runnable {
-
-
-
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, 通過設置休眠時間, 模擬訪問資源的流量大小
-
TimeUnit.MILLISECONDS.sleep(random2.nextInt( 2000));
-
} catch (InterruptedException e) {
-
// ignore
-
}
-
}
-
}
-
}
-
-
static class RunTask implements Runnable {
-
-
-
public void run() {
-
while (!stop) {
-
Entry entry = null;
-
try {
-
entry = SphU.entry(KEY);
-
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 {
-
// 隨機休眠時間<50ms, 通過設置休眠時間, 模擬訪問資源的流量大小
-
TimeUnit.MILLISECONDS.sleep(random2.nextInt( 50));
-
} catch (InterruptedException e) {
-
// ignore
-
}
-
}
-
}
-
}
-
-
static class TimerTask implements Runnable {
-
-
-
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);
-
}
-
}
-
}
要想在將變化數據展示在dashboard控制台, 啟動時需要配置:
-Dcsp.sentinel.dashboard.server=127.0.0.1:8080
-Dcsp.sentinel.api.port=8719
-Dproject.name=WarmUpFlowDemo
具體接入dashboard, 可參考上一篇博客, Sentinel基本使用--基於QPS流量控制(一), 采用默認快速失敗/直接拒絕策略控制超過閾值的流量(結合Dashboard使用)
五, 總結
上面主要講述了QPS流量控制, 采用Warm Up預熱/冷啟動方式控制突增流量, 通過在后台console觀察數據以及結合dashboard圖表的形式, 能很清晰的了解到warm up冷啟動方式控制突增流量, 保護資源, 維護系統的穩定性的.