Sentinel基本使用--基于QPS流量控制(二), 采用Warm Up预热/冷启动方式控制突增流量
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冷启动方式控制突增流量, 保护资源, 维护系统的稳定性的.