From 19db20f00d7f8334de67d03f90ead22bf998530c Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Tue, 19 May 2020 14:56:35 +0800 Subject: [PATCH] Update demo for circuit breaking (DegradeRule) Signed-off-by: Eric Zhao --- .../degrade/ExceptionCountDegradeDemo.java | 169 ------------------ ... => ExceptionRatioCircuitBreakerDemo.java} | 148 +++++++-------- ....java => SlowRatioCircuitBreakerDemo.java} | 159 ++++++++-------- 3 files changed, 155 insertions(+), 321 deletions(-) delete mode 100644 sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionCountDegradeDemo.java rename sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/{ExceptionRatioDegradeDemo.java => ExceptionRatioCircuitBreakerDemo.java} (50%) rename sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/{RtDegradeDemo.java => SlowRatioCircuitBreakerDemo.java} (51%) diff --git a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionCountDegradeDemo.java b/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionCountDegradeDemo.java deleted file mode 100644 index d0512746..00000000 --- a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionCountDegradeDemo.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.alibaba.csp.sentinel.demo.degrade; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import com.alibaba.csp.sentinel.Entry; -import com.alibaba.csp.sentinel.SphU; -import com.alibaba.csp.sentinel.Tracer; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; -import com.alibaba.csp.sentinel.util.TimeUtil; - -/** - *

- * Degrade is used when the resources are in an unstable state, these resources - * will be degraded within the next defined time window. There are three ways to - * measure whether a resource is stable or not: - *

- *

- *

- * Note: When degrading by {@link RuleConstant#DEGRADE_GRADE_EXCEPTION_COUNT}, time window - * less than 60 seconds will not work as expected. Because the exception count is - * summed by minute, when a short time window elapsed, the degradation condition - * may still be satisfied. - *

- * - * @author Carpenter Lee - */ -public class ExceptionCountDegradeDemo { - private static final String KEY = "abc"; - - private static AtomicInteger total = new AtomicInteger(); - private static AtomicInteger pass = new AtomicInteger(); - private static AtomicInteger block = new AtomicInteger(); - private static AtomicInteger bizException = new AtomicInteger(); - - private static volatile boolean stop = false; - private static final int threadCount = 1; - private static int seconds = 60 + 40; - - public static void main(String[] args) throws Exception { - tick(); - initDegradeRule(); - - for (int i = 0; i < threadCount; i++) { - Thread entryThread = new Thread(new Runnable() { - - @Override - public void run() { - int count = 0; - while (true) { - count++; - Entry entry = null; - try { - Thread.sleep(20); - entry = SphU.entry(KEY); - // token acquired, means pass - pass.addAndGet(1); - if (count % 2 == 0) { - // biz code raise an exception. - throw new RuntimeException("throw runtime "); - } - } catch (BlockException e) { - block.addAndGet(1); - } catch (Throwable t) { - bizException.incrementAndGet(); - Tracer.trace(t); - } finally { - total.addAndGet(1); - if (entry != null) { - entry.exit(); - } - } - } - } - - }); - entryThread.setName("working-thread"); - entryThread.start(); - } - - } - - private static void initDegradeRule() { - List rules = new ArrayList(); - DegradeRule rule = new DegradeRule(); - rule.setResource(KEY); - // set limit exception count to 4 - rule.setCount(4); - rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); - /** - * When degrading by {@link RuleConstant#DEGRADE_GRADE_EXCEPTION_COUNT}, time window - * less than 60 seconds will not work as expected. Because the exception count is - * summed by minute, when a short time window elapsed, the degradation condition - * may still be satisfied. - */ - rule.setTimeWindow(10); - rules.add(rule); - DegradeRuleManager.loadRules(rules); - } - - private static void tick() { - Thread timer = new Thread(new TimerTask()); - timer.setName("sentinel-timer-task"); - timer.start(); - } - - 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; - long oldBizException = 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; - - long globalBizException = bizException.get(); - long oneSecondBizException = globalBizException - oldBizException; - oldBizException = globalBizException; - - System.out.println(TimeUtil.currentTimeMillis() + ", oneSecondTotal:" + oneSecondTotal - + ", oneSecondPass:" + oneSecondPass - + ", oneSecondBlock:" + oneSecondBlock - + ", oneSecondBizException:" + oneSecondBizException); - 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() + ", bizException:" + bizException.get()); - System.exit(0); - } - } -} diff --git a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioDegradeDemo.java b/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioCircuitBreakerDemo.java similarity index 50% rename from sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioDegradeDemo.java rename to sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioCircuitBreakerDemo.java index c84756aa..f57a39df 100755 --- a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioDegradeDemo.java +++ b/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/ExceptionRatioCircuitBreakerDemo.java @@ -19,41 +19,26 @@ import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.Tracer; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry; import com.alibaba.csp.sentinel.util.TimeUtil; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** - *

- * Degrade is used when the resources are in an unstable state, these resources - * will be degraded within the next defined time window. There are three ways to - * measure whether a resource is stable or not: - *

    - *
  • - * Exception ratio: When the ratio of exception count per second and the success - * qps greats than or equals to the threshold, access to the resource will be blocked - * in the coming time window. - *
  • - *
  • - * Exception Count, see {@link ExceptionCountDegradeDemo}. - *
  • - *
  • - * For average response time, see {@link RtDegradeDemo}. - *
  • - *
- *

- * * @author jialiang.linjl + * @author Eric Zhao */ -public class ExceptionRatioDegradeDemo { +public class ExceptionRatioCircuitBreakerDemo { - private static final String KEY = "abc"; + private static final String KEY = "some_service"; private static AtomicInteger total = new AtomicInteger(); private static AtomicInteger pass = new AtomicInteger(); @@ -61,68 +46,87 @@ public class ExceptionRatioDegradeDemo { private static AtomicInteger bizException = new AtomicInteger(); private static volatile boolean stop = false; - private static final int threadCount = 1; - private static int seconds = 60 + 40; + private static int seconds = 120; public static void main(String[] args) throws Exception { - tick(); initDegradeRule(); - - for (int i = 0; i < threadCount; i++) { - Thread entryThread = new Thread(new Runnable() { - - @Override - public void run() { - int count = 0; - while (true) { - count++; - Entry entry = null; - try { - Thread.sleep(20); - entry = SphU.entry(KEY); - // token acquired, means pass - pass.addAndGet(1); - if (count % 2 == 0) { - // biz code raise an exception. - throw new RuntimeException("throw runtime "); - } - } catch (BlockException e) { - block.addAndGet(1); - } catch (Throwable t) { - bizException.incrementAndGet(); - Tracer.trace(t); - } finally { - total.addAndGet(1); - if (entry != null) { - entry.exit(); - } + registerStateChangeObserver(); + startTick(); + + final int concurrency = 8; + for (int i = 0; i < concurrency; i++) { + Thread entryThread = new Thread(() -> { + while (true) { + Entry entry = null; + try { + entry = SphU.entry(KEY); + sleep(ThreadLocalRandom.current().nextInt(5, 10)); + pass.addAndGet(1); + + // Error probability is 45% + if (ThreadLocalRandom.current().nextInt(0, 100) > 55) { + // biz code raise an exception. + throw new RuntimeException("oops"); + } + } catch (BlockException e) { + block.addAndGet(1); + sleep(ThreadLocalRandom.current().nextInt(5, 10)); + } catch (Throwable t) { + bizException.incrementAndGet(); + // It's required to record exception here manually. + Tracer.traceEntry(t, entry); + } finally { + total.addAndGet(1); + if (entry != null) { + entry.exit(); } } } - }); - entryThread.setName("working-thread"); + entryThread.setName("sentinel-simulate-traffic-task-" + i); entryThread.start(); } + } + private static void registerStateChangeObserver() { + EventObserverRegistry.getInstance().addStateChangeObserver("logging", + (prevState, newState, rule, snapshotValue) -> { + if (newState == State.OPEN) { + System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(), + TimeUtil.currentTimeMillis(), snapshotValue)); + } else { + System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(), + TimeUtil.currentTimeMillis())); + } + }); } private static void initDegradeRule() { - List rules = new ArrayList(); - DegradeRule rule = new DegradeRule(); - rule.setResource(KEY); - // set limit exception ratio to 0.1 - rule.setCount(0.1); - rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); - rule.setTimeWindow(10); - rule.setMinRequestAmount(20); + List rules = new ArrayList<>(); + DegradeRule rule = new DegradeRule(KEY) + .setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType()) + // Set ratio threshold to 50%. + .setCount(0.5d) + .setStatIntervalMs(30000) + .setMinRequestAmount(50) + // Retry timeout (in second) + .setTimeWindow(10); rules.add(rule); DegradeRuleManager.loadRules(rules); + System.out.println("Degrade rule loaded: " + rules); } - private static void tick() { + private static void sleep(int timeMs) { + try { + TimeUnit.MILLISECONDS.sleep(timeMs); + } catch (InterruptedException e) { + // ignore + } + } + + private static void startTick() { Thread timer = new Thread(new TimerTask()); - timer.setName("sentinel-timer-task"); + timer.setName("sentinel-timer-tick-task"); timer.start(); } @@ -130,16 +134,16 @@ public class ExceptionRatioDegradeDemo { @Override public void run() { long start = System.currentTimeMillis(); - System.out.println("begin to statistic!!!"); + System.out.println("Begin to run! Go go go!"); + System.out.println("See corresponding metrics.log for accurate statistic data"); + long oldTotal = 0; long oldPass = 0; long oldBlock = 0; long oldBizException = 0; while (!stop) { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - } + sleep(1000); + long globalTotal = total.get(); long oneSecondTotal = globalTotal - oldTotal; oldTotal = globalTotal; @@ -166,7 +170,7 @@ public class ExceptionRatioDegradeDemo { } long cost = System.currentTimeMillis() - start; System.out.println("time cost: " + cost + " ms"); - System.out.println("total:" + total.get() + ", pass:" + pass.get() + System.out.println("total: " + total.get() + ", pass:" + pass.get() + ", block:" + block.get() + ", bizException:" + bizException.get()); System.exit(0); } diff --git a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/RtDegradeDemo.java b/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/SlowRatioCircuitBreakerDemo.java similarity index 51% rename from sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/RtDegradeDemo.java rename to sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/SlowRatioCircuitBreakerDemo.java index c04cbf8a..b7f64567 100755 --- a/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/RtDegradeDemo.java +++ b/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/degrade/SlowRatioCircuitBreakerDemo.java @@ -17,47 +17,28 @@ package com.alibaba.csp.sentinel.demo.degrade; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import com.alibaba.csp.sentinel.util.TimeUtil; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry; +import com.alibaba.csp.sentinel.util.TimeUtil; /** - *

- * Degrade is used when the resources are in an unstable state, these resources - * will be degraded within the next defined time window. There are two ways to - * measure whether a resource is stable or not: - *

    - *
  • - * Average Response Time ('DegradeRule.Grade=RuleContants.DEGRADE_GRADE_RT'): When the - * average RT greats than or equals to the threshold ('count' in 'DegradeRule', ms), the - * resource enters a quasi-degraded state. If the RT of next coming five requests still - * exceed this threshold, this resource will be downgraded, which means that in - * the next time window(Defined in 'timeWindow', s units) all the access to this - * resource will be blocked. - *
  • - *
  • - * Exception ratio, see {@link ExceptionRatioDegradeDemo}. - *
  • - *
  • - * Exception Count, see {@link ExceptionCountDegradeDemo}. - *
  • - *
- * - *

- * - * Run this demo, and the out put will be like: + * Run this demo, and the output will be like: * *
  * 1529399827825,total:0, pass:0, block:0
  * 1529399828825,total:4263, pass:100, block:4164
- * 1529399829825,total:19179, pass:4, block:19176
- * 1529399830824,total:19806, pass:0, block:19806  //begin degrade
+ * 1529399829825,total:19179, pass:4, block:19176 // circuit breaker opens
+ * 1529399830824,total:19806, pass:0, block:19806
  * 1529399831825,total:19198, pass:0, block:19198
  * 1529399832824,total:19481, pass:0, block:19481
  * 1529399833826,total:19241, pass:0, block:19241
@@ -66,95 +47,114 @@ import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
  * 1529399836826,total:19490, pass:0, block:19492
  * 1529399837828,total:19355, pass:0, block:19355
  * 1529399838827,total:11388, pass:0, block:11388
- * 1529399839829,total:14494, pass:104, block:14390 //After 10 seconds, the system is restored, and degraded very
- * quickly
+ * 1529399839829,total:14494, pass:104, block:14390 // After 10 seconds, the system restored
  * 1529399840854,total:18505, pass:0, block:18505
  * 1529399841854,total:19673, pass:0, block:19676
  * 
* * @author jialiang.linjl + * @author Eric Zhao */ -public class RtDegradeDemo { +public class SlowRatioCircuitBreakerDemo { - private static final String KEY = "abc"; + private static final String KEY = "some_method"; + private static volatile boolean stop = false; + private static int seconds = 120; + + private static AtomicInteger total = new AtomicInteger(); 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 { - - tick(); initDegradeRule(); - - for (int i = 0; i < threadCount; i++) { - Thread entryThread = new Thread(new Runnable() { - - @Override - public void run() { - while (true) { - Entry entry = null; - try { - TimeUnit.MILLISECONDS.sleep(5); - entry = SphU.entry(KEY); - // token acquired - pass.incrementAndGet(); - // sleep 600 ms, as rt - TimeUnit.MILLISECONDS.sleep(600); - } catch (Exception e) { - block.incrementAndGet(); - } finally { - total.incrementAndGet(); - if (entry != null) { - entry.exit(); - } + registerStateChangeObserver(); + startTick(); + + int concurrency = 8; + for (int i = 0; i < concurrency; i++) { + Thread entryThread = new Thread(() -> { + while (true) { + Entry entry = null; + try { + entry = SphU.entry(KEY); + pass.incrementAndGet(); + // RT: [40ms, 60ms) + sleep(ThreadLocalRandom.current().nextInt(40, 60)); + } catch (BlockException e) { + block.incrementAndGet(); + sleep(ThreadLocalRandom.current().nextInt(5, 10)); + } finally { + total.incrementAndGet(); + if (entry != null) { + entry.exit(); } } } - }); - entryThread.setName("working-thread"); + entryThread.setName("sentinel-simulate-traffic-task-" + i); entryThread.start(); } } + private static void registerStateChangeObserver() { + EventObserverRegistry.getInstance().addStateChangeObserver("logging", + (prevState, newState, rule, snapshotValue) -> { + if (newState == State.OPEN) { + System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(), + TimeUtil.currentTimeMillis(), snapshotValue)); + } else { + System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(), + TimeUtil.currentTimeMillis())); + } + }); + } + private static void initDegradeRule() { - List rules = new ArrayList(); - DegradeRule rule = new DegradeRule(); - rule.setResource(KEY); - // set threshold rt, 10 ms - rule.setCount(10); - rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); - rule.setTimeWindow(10); + List rules = new ArrayList<>(); + DegradeRule rule = new DegradeRule(KEY) + .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()) + // Max allowed response time + .setCount(50) + // Retry timeout (in second) + .setTimeWindow(10) + // Circuit breaker opens when slow request ratio > 60% + .setSlowRatioThreshold(0.6) + .setMinRequestAmount(100) + .setStatIntervalMs(20000); rules.add(rule); + DegradeRuleManager.loadRules(rules); + System.out.println("Degrade rule loaded: " + rules); + } + + private static void sleep(int timeMs) { + try { + TimeUnit.MILLISECONDS.sleep(timeMs); + } catch (InterruptedException e) { + // ignore + } } - private static void tick() { + private static void startTick() { Thread timer = new Thread(new TimerTask()); - timer.setName("sentinel-timer-task"); + timer.setName("sentinel-timer-tick-task"); timer.start(); } static class TimerTask implements Runnable { - @Override public void run() { long start = System.currentTimeMillis(); - System.out.println("begin to statistic!!!"); + System.out.println("Begin to run! Go go go!"); + System.out.println("See corresponding metrics.log for accurate statistic data"); + long oldTotal = 0; long oldPass = 0; long oldBlock = 0; while (!stop) { - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - } + sleep(1000); long globalTotal = total.get(); long oneSecondTotal = globalTotal - oldTotal; @@ -178,10 +178,9 @@ public class RtDegradeDemo { long cost = System.currentTimeMillis() - start; System.out.println("time cost: " + cost + " ms"); - System.out.println("total:" + total.get() + ", pass:" + pass.get() + System.out.println("total: " + total.get() + ", pass:" + pass.get() + ", block:" + block.get()); System.exit(0); } } - }