Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -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; | |||
/** | |||
* <p> | |||
* 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: | |||
* <ul> | |||
* <li> | |||
* Exception count: When the exception count in the last 60 seconds greats than | |||
* or equals to the threshold, access to the resource will be blocked in the | |||
* coming time window. | |||
* </li> | |||
* <li> | |||
* Exception ratio, see {@link ExceptionRatioDegradeDemo}. | |||
* </li> | |||
* <li> | |||
* For average response time, see {@link RtDegradeDemo}. | |||
* </li> | |||
* </ul> | |||
* </p> | |||
* <p> | |||
* 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. | |||
* </p> | |||
* | |||
* @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<DegradeRule> rules = new ArrayList<DegradeRule>(); | |||
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); | |||
} | |||
} | |||
} |
@@ -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; | |||
/** | |||
* <p> | |||
* 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: | |||
* <ul> | |||
* <li> | |||
* 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. | |||
* </li> | |||
* <li> | |||
* Exception Count, see {@link ExceptionCountDegradeDemo}. | |||
* </li> | |||
* <li> | |||
* For average response time, see {@link RtDegradeDemo}. | |||
* </li> | |||
* </ul> | |||
* </p> | |||
* | |||
* @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<DegradeRule> rules = new ArrayList<DegradeRule>(); | |||
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<DegradeRule> 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); | |||
} |
@@ -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; | |||
/** | |||
* <p> | |||
* 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: | |||
* <ul> | |||
* <li> | |||
* 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. | |||
* </li> | |||
* <li> | |||
* Exception ratio, see {@link ExceptionRatioDegradeDemo}. | |||
* </li> | |||
* <li> | |||
* Exception Count, see {@link ExceptionCountDegradeDemo}. | |||
* </li> | |||
* </ul> | |||
* | |||
* </p> | |||
* | |||
* Run this demo, and the out put will be like: | |||
* Run this demo, and the output will be like: | |||
* | |||
* <pre> | |||
* 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 | |||
* </pre> | |||
* | |||
* @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<DegradeRule> rules = new ArrayList<DegradeRule>(); | |||
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<DegradeRule> 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); | |||
} | |||
} | |||
} |