zoukankan      html  css  js  c++  java
  • Sentinel基本使用--基于QPS流量控制(二), 采用Warm Up预热/冷启动方式控制突增流量

    Sentinel基本使用--基于QPS流量控制(二), 采用Warm Up预热/冷启动方式控制突增流量

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/xiongxianze/article/details/87580917

    一, Warm Up

    Sentinel的Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。warm up冷启动主要用于启动需要额外开销的场景,例如建立数据库连接等。

    二, 实例

    本文结合sentinel提供的示例, 通过dashboard控制台展示warm up方式启动流量曲线变化, 

    WarmUpFlowDemo类说明:

    1 初始化基于QPS流控规则, 流控效果使用warm up; 阈值 : 1000, 预热时间60s;

    1.  
      private static void initFlowRule() {
    2.  
      List<FlowRule> rules = new ArrayList<FlowRule>();
    3.  
      FlowRule rule1 = new FlowRule();
    4.  
      rule1.setResource(KEY);
    5.  
      // 这里设置QPS最大的阈值1000, 尽量设置大一点, 便于在监控台查看流量变化曲线
    6.  
      rule1.setCount(1000);
    7.  
      // 基于QPS流控规则
    8.  
      rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    9.  
      // 默认不区分调用来源
    10.  
      rule1.setLimitApp("default");
    11.  
      // 流控效果, 采用warm up冷启动方式
    12.  
      rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
    13.  
      // 在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
    14.  
      // warmUpPeriodSec 代表期待系统进入稳定状态的时间(即预热时长)。
    15.  
      // 这里预热时间为1min, 便于在dashboard控制台实时监控查看QPS的pass和block变化曲线
    16.  
      rule1.setWarmUpPeriodSec(60); // 默认值为10s
    17.  
       
    18.  
      rules.add(rule1);
    19.  
      FlowRuleManager.loadRules(rules);
    20.  
      }

    2 启动一个TimerTask线程, 统计每一秒的pass, block, total这三个指标;

    1.  
      static class TimerTask implements Runnable {
    2.  
       
    3.  
      @Override
    4.  
      public void run() {
    5.  
      long start = System.currentTimeMillis();
    6.  
      System.out.println("begin to statistic!!!");
    7.  
      long oldTotal = 0;
    8.  
      long oldPass = 0;
    9.  
      long oldBlock = 0;
    10.  
      while (!stop) {
    11.  
      try {
    12.  
      TimeUnit.SECONDS.sleep(1);
    13.  
      } catch (InterruptedException e) {
    14.  
      }
    15.  
       
    16.  
      long globalTotal = total.get();
    17.  
      long oneSecondTotal = globalTotal - oldTotal;
    18.  
      oldTotal = globalTotal;
    19.  
       
    20.  
      long globalPass = pass.get();
    21.  
      long oneSecondPass = globalPass - oldPass;
    22.  
      oldPass = globalPass;
    23.  
       
    24.  
      long globalBlock = block.get();
    25.  
      long oneSecondBlock = globalBlock - oldBlock;
    26.  
      oldBlock = globalBlock;
    27.  
       
    28.  
      System.out.println("currentTimeMillis:" + TimeUtil.currentTimeMillis() + ", totalSeconds:"
    29.  
      + TimeUtil.currentTimeMillis() / 1000 + ", currentSecond:"
    30.  
      + (TimeUtil.currentTimeMillis() / 1000) % 60 + ", total:" + oneSecondTotal
    31.  
      + ", pass:" + oneSecondPass + ", block:" + oneSecondBlock);
    32.  
       
    33.  
      if (seconds-- <= 0) {
    34.  
      stop = true;
    35.  
      }
    36.  
      }
    37.  
       
    38.  
      long cost = System.currentTimeMillis() - start;
    39.  
      System.out.println("time cost: " + cost + " ms");
    40.  
      System.out.println("total:" + total.get() + ", pass:" + pass.get() + ", block:" + block.get());
    41.  
      System.exit(0);
    42.  
      }
    43.  
      }

    3 同时启动三个WarmUpTask线程, 设置其休眠时间小于2s, 使系统访问资源处于一个较低的流量 .

    ①同时启动3个WarmUpTask线程

    1.  
      for (int i = 0; i < 3; i++) {
    2.  
      Thread t = new Thread(new WarmUpTask());
    3.  
      t.setName("sentinel-warmup-task");
    4.  
      t.start();
    5.  
      }

    ②WarmUpTask线程休眠小于2s, 通过控制休眠时间, 达到控制访问资源的流量处于一个较低的水平.

    1.  
      static class WarmUpTask implements Runnable {
    2.  
       
    3.  
      @Override
    4.  
      public void run() {
    5.  
      while (!stop) {
    6.  
      Entry entry = null;
    7.  
      try {
    8.  
      entry = SphU.entry(KEY);
    9.  
      // token acquired, means pass
    10.  
      pass.addAndGet(1);
    11.  
      } catch (BlockException e1) {
    12.  
      block.incrementAndGet();
    13.  
      } catch (Exception e2) {
    14.  
      // biz exception
    15.  
      } finally {
    16.  
      total.incrementAndGet();
    17.  
      if (entry != null) {
    18.  
      entry.exit();
    19.  
      }
    20.  
      }
    21.  
      Random random2 = new Random();
    22.  
      try {
    23.  
      // 随机休眠时间<2s, 通过设置休眠时间, 模拟访问资源的流量大小
    24.  
      TimeUnit.MILLISECONDS.sleep(random2.nextInt(2000));
    25.  
      } catch (InterruptedException e) {
    26.  
      // ignore
    27.  
      }
    28.  
      }
    29.  
      }
    30.  
      }

    4 WarmUpTask线程运行20s后,再同时启动100个线程, 设置其休眠时间小于50ms, 这样就模拟造成了访问资源的流量突增, 一是可以查看后台console观察流量变化数值, 而是查看监控台的实时监控, 能比较直观的看见warm up过程.

    ①20s后, 再同时启动100个线程

    1.  
      // 20s开始有突增的流量进来, 访问资源
    2.  
      Thread.sleep(20000);

    ②再同时启动100个线程, 模拟突增的流量访问资源

    1.  
      // 创建一个100线程, 模拟突增的流量访问被保护的资源
    2.  
      for (int i = 0; i < threadCount; i++) {
    3.  
      Thread t = new Thread(new RunTask());
    4.  
      t.setName("sentinel-run-task");
    5.  
      t.start();
    6.  
      }

    ③RunTask线程休眠时间小于50ms, 这样每个线程就能多次的访问资源, 模拟造成资源被突增的流量访问. 这样对资源的访问流量就处于一个较高的水平.

    1.  
      static class RunTask implements Runnable {
    2.  
       
    3.  
      @Override
    4.  
      public void run() {
    5.  
      while (!stop) {
    6.  
      Entry entry = null;
    7.  
      try {
    8.  
      entry = SphU.entry(KEY);
    9.  
      pass.addAndGet(1);
    10.  
      } catch (BlockException e1) {
    11.  
      block.incrementAndGet();
    12.  
      } catch (Exception e2) {
    13.  
      // biz exception
    14.  
      } finally {
    15.  
      total.incrementAndGet();
    16.  
      if (entry != null) {
    17.  
      entry.exit();
    18.  
      }
    19.  
      }
    20.  
      Random random2 = new Random();
    21.  
      try {
    22.  
      // 随机休眠时间<50ms, 通过设置休眠时间, 模拟访问资源的流量大小
    23.  
      TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
    24.  
      } catch (InterruptedException e) {
    25.  
      // ignore
    26.  
      }
    27.  
      }
    28.  
      }
    29.  
      }

    三, 后台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. 

    完整代码:

    1.  
      public class WarmUpFlowDemo {
    2.  
       
    3.  
      private static final String KEY = "abc";
    4.  
       
    5.  
      private static AtomicInteger pass = new AtomicInteger();
    6.  
      private static AtomicInteger block = new AtomicInteger();
    7.  
      private static AtomicInteger total = new AtomicInteger();
    8.  
       
    9.  
      private static volatile boolean stop = false;
    10.  
       
    11.  
      private static final int threadCount = 100;
    12.  
      private static int seconds = 60 + 40;
    13.  
       
    14.  
      public static void main(String[] args) throws Exception {
    15.  
      initFlowRule();
    16.  
      // trigger Sentinel internal init
    17.  
      Entry entry = null;
    18.  
      try {
    19.  
      entry = SphU.entry(KEY);
    20.  
      } catch (Exception e) {
    21.  
      } finally {
    22.  
      if (entry != null) {
    23.  
      entry.exit();
    24.  
      }
    25.  
      }
    26.  
       
    27.  
      Thread timer = new Thread(new TimerTask());
    28.  
      timer.setName("sentinel-timer-task");
    29.  
      timer.start();
    30.  
       
    31.  
      // first make the system run on a very low condition
    32.  
      // 创建3个线程, 模拟一个系统处于一个低水平流量
    33.  
      for (int i = 0; i < 3; i++) {
    34.  
      Thread t = new Thread(new WarmUpTask());
    35.  
      t.setName("sentinel-warmup-task");
    36.  
      t.start();
    37.  
      }
    38.  
       
    39.  
      // 20s开始有突增的流量进来, 访问资源
    40.  
      Thread.sleep(20000);
    41.  
       
    42.  
      /*
    43.  
      * Start more thread to simulate more qps. Since we use {@link RuleConstant.CONTROL_BEHAVIOR_WARM_UP} as {@link
    44.  
      * FlowRule#controlBehavior}, real passed qps will increase to {@link FlowRule#count} in {@link
    45.  
      * FlowRule#warmUpPeriodSec} seconds.
    46.  
      */
    47.  
      // 创建一个100线程, 模拟突增的流量访问被保护的资源
    48.  
      for (int i = 0; i < threadCount; i++) {
    49.  
      Thread t = new Thread(new RunTask());
    50.  
      t.setName("sentinel-run-task");
    51.  
      t.start();
    52.  
      }
    53.  
      }
    54.  
       
    55.  
      private static void initFlowRule() {
    56.  
      List<FlowRule> rules = new ArrayList<FlowRule>();
    57.  
      FlowRule rule1 = new FlowRule();
    58.  
      rule1.setResource(KEY);
    59.  
      // 设置最大阈值为20
    60.  
      // rule1.setCount(20);
    61.  
      // 这里设置QPS最大的阈值1000, 便于查看变化曲线
    62.  
      rule1.setCount(1000);
    63.  
      rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    64.  
      rule1.setLimitApp("default");
    65.  
      rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
    66.  
      // 在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
    67.  
      // warmUpPeriodSec 代表期待系统进入稳定状态的时间(即预热时长)。
    68.  
      // 这里预热时间为1min, 便于在dashboard控制台实时监控查看QPS的pass和block变化曲线
    69.  
      rule1.setWarmUpPeriodSec(60); // 默认值为10s
    70.  
       
    71.  
      rules.add(rule1);
    72.  
      FlowRuleManager.loadRules(rules);
    73.  
      }
    74.  
       
    75.  
      static class WarmUpTask implements Runnable {
    76.  
       
    77.  
      @Override
    78.  
      public void run() {
    79.  
      while (!stop) {
    80.  
      Entry entry = null;
    81.  
      try {
    82.  
      entry = SphU.entry(KEY);
    83.  
      // token acquired, means pass
    84.  
      pass.addAndGet(1);
    85.  
      } catch (BlockException e1) {
    86.  
      block.incrementAndGet();
    87.  
      } catch (Exception e2) {
    88.  
      // biz exception
    89.  
      } finally {
    90.  
      total.incrementAndGet();
    91.  
      if (entry != null) {
    92.  
      entry.exit();
    93.  
      }
    94.  
      }
    95.  
      Random random2 = new Random();
    96.  
      try {
    97.  
      // 随机休眠时间<2s, 通过设置休眠时间, 模拟访问资源的流量大小
    98.  
      TimeUnit.MILLISECONDS.sleep(random2.nextInt(2000));
    99.  
      } catch (InterruptedException e) {
    100.  
      // ignore
    101.  
      }
    102.  
      }
    103.  
      }
    104.  
      }
    105.  
       
    106.  
      static class RunTask implements Runnable {
    107.  
       
    108.  
      @Override
    109.  
      public void run() {
    110.  
      while (!stop) {
    111.  
      Entry entry = null;
    112.  
      try {
    113.  
      entry = SphU.entry(KEY);
    114.  
      pass.addAndGet(1);
    115.  
      } catch (BlockException e1) {
    116.  
      block.incrementAndGet();
    117.  
      } catch (Exception e2) {
    118.  
      // biz exception
    119.  
      } finally {
    120.  
      total.incrementAndGet();
    121.  
      if (entry != null) {
    122.  
      entry.exit();
    123.  
      }
    124.  
      }
    125.  
      Random random2 = new Random();
    126.  
      try {
    127.  
      // 随机休眠时间<50ms, 通过设置休眠时间, 模拟访问资源的流量大小
    128.  
      TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
    129.  
      } catch (InterruptedException e) {
    130.  
      // ignore
    131.  
      }
    132.  
      }
    133.  
      }
    134.  
      }
    135.  
       
    136.  
      static class TimerTask implements Runnable {
    137.  
       
    138.  
      @Override
    139.  
      public void run() {
    140.  
      long start = System.currentTimeMillis();
    141.  
      System.out.println("begin to statistic!!!");
    142.  
      long oldTotal = 0;
    143.  
      long oldPass = 0;
    144.  
      long oldBlock = 0;
    145.  
      while (!stop) {
    146.  
      try {
    147.  
      TimeUnit.SECONDS.sleep(1);
    148.  
      } catch (InterruptedException e) {
    149.  
      }
    150.  
       
    151.  
      long globalTotal = total.get();
    152.  
      long oneSecondTotal = globalTotal - oldTotal;
    153.  
      oldTotal = globalTotal;
    154.  
       
    155.  
      long globalPass = pass.get();
    156.  
      long oneSecondPass = globalPass - oldPass;
    157.  
      oldPass = globalPass;
    158.  
       
    159.  
      long globalBlock = block.get();
    160.  
      long oneSecondBlock = globalBlock - oldBlock;
    161.  
      oldBlock = globalBlock;
    162.  
       
    163.  
      System.out.println("currentTimeMillis:" + TimeUtil.currentTimeMillis() + ", totalSeconds:"
    164.  
      + TimeUtil.currentTimeMillis() / 1000 + ", currentSecond:"
    165.  
      + (TimeUtil.currentTimeMillis() / 1000) % 60 + ", total:" + oneSecondTotal
    166.  
      + ", pass:" + oneSecondPass + ", block:" + oneSecondBlock);
    167.  
       
    168.  
      if (seconds-- <= 0) {
    169.  
      stop = true;
    170.  
      }
    171.  
      }
    172.  
       
    173.  
      long cost = System.currentTimeMillis() - start;
    174.  
      System.out.println("time cost: " + cost + " ms");
    175.  
      System.out.println("total:" + total.get() + ", pass:" + pass.get() + ", block:" + block.get());
    176.  
      try {
    177.  
      TimeUnit.SECONDS.sleep(60);
    178.  
      } catch (InterruptedException e) {
    179.  
      // TODO Auto-generated catch block
    180.  
      e.printStackTrace();
    181.  
      }
    182.  
      System.exit(0);
    183.  
      }
    184.  
      }
    185.  
      }

    要想在将变化数据展示在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冷启动方式控制突增流量, 保护资源, 维护系统的稳定性的.

  • 相关阅读:
    poj1978
    poj3505
    免费专利讲座
    没有找到MFC80UD.DLL"没有找到MFC80UD.DLL,
    新手学C++/CLI及C#
    怎样激发技术人员的创新力?
    关于大型asp.net应用系统的架构—如何做到高性能高可伸缩性
    绝好的一套针对初学者的JavaScript教程
    VC如何将自身进程提升至管理员权限
    如何寻找优秀的专利代理人
  • 原文地址:https://www.cnblogs.com/think90/p/11443372.html
Copyright © 2011-2022 走看看