Sentinel源码—6.熔断降级和数据统计的实现一
大纲
1.DegradeSlot实现熔断降级的原理与源码
2.Sentinel数据指标统计的滑动窗口算法
1.DegradeSlot实现熔断降级的原理与源码
(1)熔断降级规则DegradeRule的配置Demo
(2)注册熔断降级监听器和加载熔断降级规则
(3)DegradeSlot根据熔断降级规则对请求进行验证
(1)熔断降级规则DegradeRule的配置Demo
首先熔断降级规则的应用场景有如下两种:
场景一:在微服务架构中,当一个服务出现问题时,可以通过配置熔断降级规则,防止故障扩散,保护整个系统的稳定性。
场景二:在调用第三方API时,可以配置熔断降级规则,避免因第三方API不稳定导致自身系统不稳定。
然后从下图可知,熔断降级规则包含以下属性:
属性一:熔断策略(grade)
这表示的是熔断降级规则的类型,取值范围分别是:
RuleConstant.DEGRADE_GRADE_RT(慢调用比例)
RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO(异常比例)
RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT(异常数)
其中,默认下的熔断降级规则是基于慢调用比例策略的,也就是默认值为:
RuleConstant.DEGRADE_GRADE_RT
属性二:熔断降级的阈值(count)
DegradeRule.count属性的具体含义取决于DegradeRule.grade属性的值。
如果grade为慢调用比例,则count表示慢调用比例阈值。
如果grade为异常比例,则count表示异常比例阈值。
如果grade为异常数,则count表示异常数阈值。
属性三:熔断时长(timeWindow)
这表示的是熔断降级发生后的降级持续时间,在这段时间内对应的资源将被降级。
属性四:最小请求数(minRequestAmount)
这表示的是熔断降级统计周期内的最小请求总数。仅当周期内的请求总数达到此值时,才会根据grade和count进行熔断降级。默认值为:
RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT
属性五:慢调用比例阈值(slowRatioThreshold)
该属性当grade为慢调用比例时生效,取值范围为0到1之间的小数,表示慢调用请求占总请求的比例。
属性六:统计时长(statIntervalMs)
这表示的是熔断降级统计周期(单位:毫秒),默认值为1000毫秒(1秒)。在这个周期内,Sentinel会对请求进行统计,以判断是否要进行熔断降级。
public class DegradeRule extends AbstractRule {//熔断策略,表示的是熔断降级规则的类型private int grade = RuleConstant.DEGRADE_GRADE_RT;//熔断降级的阈值,具体含义取决于DegradeRule.grade属性的值//如果grade为慢调用比例,则count表示慢调用比例阈值//如果grade为异常比例,则count表示异常比例阈值//如果grade为异常数,则count表示异常数阈值private double count;//熔断时长,即熔断降级发生后的降级持续时间,在这段时间内对应的资源将被降级private int timeWindow;//最小请求数,仅当周期内的请求总数达到此值时,才会根据grade和count进行熔断降级private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;//慢调用比例阈值,仅当grade为慢调用比例时生效private double slowRatioThreshold = 1.0d;//统计时长,熔断降级统计周期,在这个周期内,Sentinel会对请求进行统计,以判断是否要进行熔断降级private int statIntervalMs = 1000;...
}
接着如下便是DegradeRule的配置Demo:
//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 // 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
//1529399834826,total:17276, pass:0, block:17276
//1529399835826,total:18722, pass:0, block:18722
//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 restored
//1529399840854,total:18505, pass:0, block:18505
//1529399841854,total:19673, pass:0, block:19676
public class SlowRatioCircuitBreakerDemo {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();public static void main(String[] args) throws Exception {initDegradeRule();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("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 rule = new DegradeRule(KEY).setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())//Max allowed response time.setCount(50).setTimeWindow(10)//Retry timeout (in second).setSlowRatioThreshold(0.6)//Circuit breaker opens when slow request ratio > 60%.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 startTick() {Thread timer = new Thread(new TimerTask());timer.setName("sentinel-timer-tick-task");timer.start();}static class TimerTask implements Runnable {@Overridepublic void run() {long start = System.currentTimeMillis();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) {sleep(1000);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(TimeUtil.currentTimeMillis() + ", 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);}}
}
(2)注册熔断降级监听器和加载熔断降级规则
一.Sentinel监听器模式的核心代码回顾
二.注册熔断降级监听器和加载熔断降级规则
三.用于实现Sentinel熔断降级功能的熔断器接口
一.Sentinel监听器模式的核心代码回顾
Sentinel监听器模式会包含三大角色:
角色一:监听器PropertyListener
角色二:监听器管理器SentinelProperty
角色三:规则管理器RuleManager
首先,规则管理器RuleManager在初始化时,会调用监听器管理器SentinelProperty的addListener()方法将监听器PropertyListener注册到监听器管理器SentinelProperty上。
然后,使用方使用具体的规则时,可以通过调用规则管理器RuleManager的loadRules()方法加载规则。加载规则时会调用监听器管理器SentinelProperty的updateValue()方法通知每一个监听器PropertyListener,即通过监听器PropertyListener的configUpdate()方法把规则加载到规则管理器的本地中。
二.注册熔断降级监听器和加载熔断降级规则
DegradeRuleManager中有两个全局的HashMap:一个是用于存放资源和熔断器的对应关系的HashMap—circuitBreakers,另一个是用于存放资源和熔断规则的对应关系的HashMap—ruleMap。
其中熔断器是由熔断策略DegradeRule.grade来决定的。如果熔断策略是慢调用比例,则熔断器是ResponseTimeCircuitBreaker。如果熔断策略是异常比例和异常数,则熔断器是ExceptionCircuitBreaker。
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();private T value = null;...//添加监听器到集合@Overridepublic void addListener(PropertyListener<T> listener) {listeners.add(listener);//回调监听器的configLoad()方法初始化规则配置listener.configLoad(value);}//更新值@Overridepublic boolean updateValue(T newValue) {//如果值没变化,直接返回if (isEqual(value, newValue)) {return false;}RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);//如果值发生了变化,则遍历监听器,回调监听器的configUpdate()方法更新对应的值value = newValue;for (PropertyListener<T> listener : listeners) {listener.configUpdate(newValue);}return true;}...
}public final class DegradeRuleManager {//用于存放资源和熔断器的对应关系的HashMap,其中熔断器是由熔断策略DegradeRule.grade来决定的private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new HashMap<>();//用于存放资源和熔断规则的对应关系的HashMapprivate static volatile Map<String, Set<DegradeRule>> ruleMap = new HashMap<>();private static final RulePropertyListener LISTENER = new RulePropertyListener();private static SentinelProperty<List<DegradeRule>> currentProperty = new DynamicSentinelProperty<>();static {currentProperty.addListener(LISTENER);}...private static class RulePropertyListener implements PropertyListener<List<DegradeRule>> {@Overridepublic void configUpdate(List<DegradeRule> conf) {reloadFrom(conf);RecordLog.info("[DegradeRuleManager] Degrade rules has been updated to: {}", ruleMap);}@Overridepublic void configLoad(List<DegradeRule> conf) {reloadFrom(conf);RecordLog.info("[DegradeRuleManager] Degrade rules loaded: {}", ruleMap);}private synchronized void reloadFrom(List<DegradeRule> list) {//构建熔断器Map<String, List<CircuitBreaker>> cbs = buildCircuitBreakers(list);Map<String, Set<DegradeRule>> rm = new HashMap<>(cbs.size());for (Map.Entry<String, List<CircuitBreaker>> e : cbs.entrySet()) {assert e.getValue() != null && !e.getValue().isEmpty();Set<DegradeRule> rules = new HashSet<>(e.getValue().size());for (CircuitBreaker cb : e.getValue()) {rules.add(cb.getRule());}rm.put(e.getKey(), rules);}DegradeRuleManager.circuitBreakers = cbs;DegradeRuleManager.ruleMap = rm;}private Map<String, List<CircuitBreaker>> buildCircuitBreakers(List<DegradeRule> list) {Map<String, List<CircuitBreaker>> cbMap = new HashMap<>(8);if (list == null || list.isEmpty()) {return cbMap;}for (DegradeRule rule : list) {if (!isValidRule(rule)) {RecordLog.warn("[DegradeRuleManager] Ignoring invalid rule when loading new rules: {}", rule);continue;}if (StringUtil.isBlank(rule.getLimitApp())) {rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);}CircuitBreaker cb = getExistingSameCbOrNew(rule);if (cb == null) {RecordLog.warn("[DegradeRuleManager] Unknown circuit breaking strategy, ignoring: {}", rule);continue;}String resourceName = rule.getResource();List<CircuitBreaker> cbList = cbMap.get(resourceName);if (cbList == null) {cbList = new ArrayList<>();cbMap.put(resourceName, cbList);}cbList.add(cb);}return cbMap;}}private static CircuitBreaker getExistingSameCbOrNew(DegradeRule rule) {List<CircuitBreaker> cbs = getCircuitBreakers(rule.getResource());if (cbs == null || cbs.isEmpty()) {return newCircuitBreakerFrom(rule);}for (CircuitBreaker cb : cbs) {if (rule.equals(cb.getRule())) {//Reuse the circuit breaker if the rule remains unchanged.return cb;}}return newCircuitBreakerFrom(rule);}static List<CircuitBreaker> getCircuitBreakers(String resourceName) {return circuitBreakers.get(resourceName);}//Create a circuit breaker instance from provided circuit breaking rule.private static CircuitBreaker newCircuitBreakerFrom(DegradeRule rule) {switch (rule.getGrade()) {case RuleConstant.DEGRADE_GRADE_RT:return new ResponseTimeCircuitBreaker(rule);case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:return new ExceptionCircuitBreaker(rule);default:return null;}}
}
三.用于实现Sentinel熔断降级功能的熔断器接口
进行熔断降级规则验证时,就会根据熔断降级规则的熔断策略,选择对应的熔断器CircuitBreaker。再通过熔断器CircuitBreaker的tryPass()接口,尝试通过当前请求。
//熔断器接口,用于实现Sentinel的熔断降级功能
public interface CircuitBreaker {//Get the associated circuit breaking rule.//获取当前熔断器对应的熔断降级规则DegradeRule getRule();//Acquires permission of an invocation only if it is available at the time of invoking.//尝试通过熔断器//如果熔断器处于关闭状态(CLOSED),则允许请求通过;//如果处于打开状态(OPEN),则拒绝请求;//如果处于半开状态(HALF_OPEN),则根据规则允许部分请求通过;boolean tryPass(Context context);//Get current state of the circuit breaker.//获取当前熔断器的状态(OPEN, HALF_OPEN, CLOSED)State currentState();//Record a completed request with the context and handle state transformation of the circuit breaker.//Called when a passed invocation finished.//在请求完成后调用此方法,用于更新熔断器的统计数据void onRequestComplete(Context context);//Circuit breaker state.enum State {//In OPEN state, all requests will be rejected until the next recovery time point.//表示熔断器处于打开状态,此时会拒绝所有请求OPEN,//In HALF_OPEN state, the circuit breaker will allow a "probe" invocation.//If the invocation is abnormal according to the strategy (e.g. it's slow), //the circuit breaker will re-transform to the OPEN state and wait for the next recovery time point;//otherwise the resource will be regarded as "recovered" and the circuit breaker will cease cutting off requests and transform to CLOSED state. //表示熔断器处于半开状态,此时允许部分请求通过,以检测系统是否已经恢复正常HALF_OPEN,//In CLOSED state, all requests are permitted. //When current metric value exceeds the threshold, the circuit breaker will transform to OPEN state.//表示熔断器处于关闭状态,此时允许所有请求通过CLOSED}
}public class ResponseTimeCircuitBreaker extends AbstractCircuitBreaker {......
}public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {......
}
(3)DegradeSlot根据熔断降级规则对请求进行验证
一.entry()方法会对请求进行熔断降级的规则验证
二.exit()方法会触发改变熔断器的状态
开始对请求进行规则验证时,需要调用SphU的entry()方法。完成对请求的规则验证后,也需要调用Entry的exit()方法。
一.entry()方法会对请求进行熔断降级的规则验证
在DegradeSlot的entry()方法中,执行熔断降级规则验证的是DegradeSlot的performChecking()方法。该方法首先会根据资源名称从DegradeRuleManager中获取熔断器,然后调用每个熔断器的tryPass()方法判断熔断器开关是否已打开来验证。如果验证通过,则返回true表示放行请求。如果验证不通过,则返回false表示拦截请求。
在判断熔断器开关是否已打开的AbstractCircuitBreaker的tryPass()方法中,首先会判断熔断器是否是关闭状态。如果是关闭状态,则代表没打开熔断器,于是会直接返回true放行请求。如果是打开状态,则要继续判断当前请求是否已达熔断器恢复时间。如果当前请求已达熔断器恢复时间,也就是当前时间大于下次尝试恢复的时间,且成功将熔断器状态从OPEN变为HALF_OPEN,则放行请求,否则拒绝。
@Spi(order = Constants.ORDER_DEGRADE_SLOT)
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {//验证熔断规则的逻辑performChecking(context, resourceWrapper);fireEntry(context, resourceWrapper, node, count, prioritized, args);}void performChecking(Context context, ResourceWrapper r) throws BlockException {//先根据资源名称获取对应的熔断器,也就是从DegradeRuleManager中的Map<String, List<CircuitBreaker>>类型的circuitBreakers获取List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {return;}//调用每个熔断器的tryPass()方法验证当前请求是否可以通过for (CircuitBreaker cb : circuitBreakers) {if (!cb.tryPass(context)) {throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());}}}...
}public abstract class AbstractCircuitBreaker implements CircuitBreaker {protected final DegradeRule rule;private final EventObserverRegistry observerRegistry;//熔断器当前的开关状态protected final AtomicReference<State> currentState = new AtomicReference<>(State.CLOSED);//下一次尝试恢复的时间protected volatile long nextRetryTimestamp;...@Overridepublic boolean tryPass(Context context) {//首先判断熔断器是否是关闭状态//如果是关闭状态则代表根本没打开熔断器,也就不涉及熔断了,因此直接返回true放行当前请求if (currentState.get() == State.CLOSED) {return true;}//如果熔断器是打开状态,那么就要进行如下逻辑判断if (currentState.get() == State.OPEN) {//如果当前系统时间大于等于下一次尝试恢复的时间,即已到达可尝试恢复的时间且成功设置当前熔断器状态为半开启,则可以放行当前请求//也就是如果此次请求已达到了熔断器恢复时间,并且将熔断器的状态从打开变为半开启,则放行,反之拒绝return retryTimeoutArrived() && fromOpenToHalfOpen(context);}return false;}protected boolean retryTimeoutArrived() {//当前时间是否大于下一次尝试恢复的时间return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;}protected boolean fromOpenToHalfOpen(Context context) {//将当前熔断器的状态从OPEN变为HALF_OPENif (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {//通过观察者模式通知各个观察者notifyObservers(State.OPEN, State.HALF_OPEN, null);...return true;}return false;}private void notifyObservers(CircuitBreaker.State prevState, CircuitBreaker.State newState, Double snapshotValue) {for (CircuitBreakerStateChangeObserver observer : observerRegistry.getStateChangeObservers()) {observer.onStateChange(prevState, newState, rule, snapshotValue);}}...
}
二.exit()方法会触发改变熔断器的状态
问题一:熔断器的tryPass()方法一开始就会判断熔断器的状态,那么熔断器何时打开、何时关闭?
答:这个会在配置熔断降级规则DegradeRule时指定,如下图就指定了:如果最近10000ms内,10个请求中有2个是异常的,则触发熔断。
问题二:如果熔断器状态为打开,判断时会比较下一次尝试恢复时间和当前时间。如果当前时间大于下一次尝试恢复的时间,意味着请求已超过熔断时间,即当前请求不再处于熔断时间段内,因此可以放行。那么下一次尝试恢复的时间nextRetryTimestamp会在何时更新?
异常数据的采集和下次恢复时间的更新会由DegradeSlot的exit()方法触发。因为开始对请求进行规则验证时,会调用SphU的entry()方法。但完成对请求的规则验证后,则会调用Entry的exit()方法,而Entry的exit()方法最终就会执行到DegradeSlot的exit()方法。
在DegradeSlot的exit()方法中,就会调用熔断器的onRequestComplete()方法来进行计数,并改变熔断器的状态。
比如在执行ExceptionCircuitBreaker的onRequestComplete()方法中,会先统计异常数errorCount和总请求数totalCount,然后根据熔断降级的规则判断是否达到打开或关闭熔断器的阈值,最后执行比如AbstractCircuitBreaker的transformToOpen()方法打开熔断器。
AbstractCircuitBreaker.transformToOpen()方法的主要工作是:首先将当前熔断器状态变更为OPEN,然后更新下一次尝试恢复时间nextRetryTimestamp,最后通过观察者设计模式通知各个观察者。
@Spi(order = Constants.ORDER_DEGRADE_SLOT)
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {...@Overridepublic void exit(Context context, ResourceWrapper r, int count, Object... args) {Entry curEntry = context.getCurEntry();if (curEntry.getBlockError() != null) {fireExit(context, r, count, args);return;}List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {fireExit(context, r, count, args);return;}//如果没报错,那么就调用熔断器的onRequestComplete()方法来计数if (curEntry.getBlockError() == null) {// passed requestfor (CircuitBreaker circuitBreaker : circuitBreakers) {circuitBreaker.onRequestComplete(context);}}fireExit(context, r, count, args);}...
}public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {...@Overridepublic void onRequestComplete(Context context) {Entry entry = context.getCurEntry();if (entry == null) {return;}Throwable error = entry.getError();//获取当前值SimpleErrorCounter counter = stat.currentWindow().value();//如果此次请求报错了,则将errorCount + 1if (error != null) {counter.getErrorCount().add(1);}//将totalCount总数 + 1,用于计算异常比例counter.getTotalCount().add(1);handleStateChangeWhenThresholdExceeded(error);}private void handleStateChangeWhenThresholdExceeded(Throwable error) {//如果当前熔断器已经打开了,则直接返回if (currentState.get() == State.OPEN) {return;}//如果当前熔断器是半开启状态if (currentState.get() == State.HALF_OPEN) {//如果本次请求没出现异常,则代表可以关闭熔断器了if (error == null) {//调用AbstractCircuitBreaker.fromHalfOpenToClose()关闭熔断器fromHalfOpenToClose();} else {//如果本次请求还是异常,就继续熔断//即调用AbstractCircuitBreaker.fromHalfOpenToOpen()方法打开熔断器fromHalfOpenToOpen(1.0d);}return;}List<SimpleErrorCounter> counters = stat.values();//异常数量long errCount = 0;//请求总数long totalCount = 0;for (SimpleErrorCounter counter : counters) {errCount += counter.errorCount.sum();totalCount += counter.totalCount.sum();}//如果请求总数没超过最小请求数,那直接放行if (totalCount < minRequestAmount) {return;}double curCount = errCount;//熔断策略为异常比例if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {//计算百分比curCount = errCount * 1.0d / totalCount;}//当错误率或者错误数大于阈值,则打开熔断器if (curCount > threshold) {//调用AbstractCircuitBreaker.transformToOpen()方法打开熔断器transformToOpen(curCount);}}...
}public abstract class AbstractCircuitBreaker implements CircuitBreaker {private final EventObserverRegistry observerRegistry;//熔断器当前的开关状态protected final AtomicReference<State> currentState = new AtomicReference<>(State.CLOSED);//下一次尝试恢复的时间protected volatile long nextRetryTimestamp;...protected void transformToOpen(double triggerValue) {State cs = currentState.get();switch (cs) {case CLOSED:fromCloseToOpen(triggerValue);break;case HALF_OPEN:fromHalfOpenToOpen(triggerValue);break;default:break;}}protected boolean fromHalfOpenToClose() {if (currentState.compareAndSet(State.HALF_OPEN, State.CLOSED)) {resetStat();notifyObservers(State.HALF_OPEN, State.CLOSED, null);return true;}return false;}protected boolean fromHalfOpenToOpen(double snapshotValue) {if (currentState.compareAndSet(State.HALF_OPEN, State.OPEN)) {updateNextRetryTimestamp();notifyObservers(State.HALF_OPEN, State.OPEN, snapshotValue);return true;}return false;}private void notifyObservers(CircuitBreaker.State prevState, CircuitBreaker.State newState, Double snapshotValue) {for (CircuitBreakerStateChangeObserver observer : observerRegistry.getStateChangeObservers()) {observer.onStateChange(prevState, newState, rule, snapshotValue);}}...
}
(4)总结
一.Sentinel熔断降级的两种熔断策略
二.Sentinel熔断降级的流程
三.熔断器的三个状态
四.熔断器三个状态之间的流转过程
一.Sentinel熔断降级的两种熔断策略
策略一:异常熔断器ExceptionCircuitBreaker
异常熔断器关注错误数量、错误比例,其核心功能是在请求结束时更新计数器,统计异常数和总请求数。当达到阈值时,熔断器的状态从CLOSED变为OPEN。
策略二:慢调用比例熔断器ResponseTimeCircuitBreaker
慢调用比例熔断器关注响应时间RT。它计算请求结束时间与请求开始时间的差值,然后与用户设置的阈值比较。若达到阈值,熔断器状态将从CLOSED变为OPEN。
二.Sentinel熔断降级的流程
步骤一:计数
比如统计错误数、响应时间rt。
步骤二:对比
将计数结果和用户设置的熔断阈值做对比,如达到阈值则打开熔断器。
步骤三:验证
请求进来时就可以直接判断熔断器是否是打开状态。如果是打开状态,则直接拒绝请求。如果是关闭状态,则直接放行请求。如果是半打开状态,则进行二次验证,看看是否能放行请求。
三.熔断器的三个状态
状态一:CLOSED,关闭状态
当熔断器处于CLOSED状态时,表示系统正常运行,没有发生熔断。此时,熔断器会对请求进行正常计数和统计。如果统计结果表明异常数/比例或者慢调用比例超过了预设阈值,熔断器将切换至OPEN状态,触发熔断。
状态二:OPEN,打开状态
当熔断器处于OPEN状态时,系统进入熔断状态。在这个状态下,熔断器会拒绝所有新的请求,直接返回预定义的降级策略。在熔断器打开一段时间后(通常由用户设置),熔断器会尝试从OPEN状态切换到HALF_OPEN状态,看系统是否已恢复。
状态三:HALF_OPEN,半开启状态
当熔断器处于HALF_OPEN状态时,系统将允许有限数量的请求通过。如果这些请求成功,熔断器将认为系统已经恢复,然后切回CLOSED状态。如果这些请求异常,熔断器会认为系统未恢复,切回OPEN状态继续熔断。
四.熔断器三个状态之间的流转过程
过程一:从CLOSED到OPEN
当异常数/比例或慢调用比例超过阈值时。
过程二:从OPEN到HALF_OPEN
在熔断器打开一段时间后,尝试恢复系统。
过程三:从HALF_OPEN到CLOSED
当允许的有限数量请求成功时。
过程四:从HALF_OPEN到OPEN
当允许的有限数量请求仍然出现异常时,HALF_OPEN就好比一个中间态。
这种流转机制确保了在系统出现问题时,熔断器能够自动进行熔断保护,同时在系统恢复后能够及时恢复正常运行。
相关文章:
Sentinel源码—6.熔断降级和数据统计的实现一
大纲 1.DegradeSlot实现熔断降级的原理与源码 2.Sentinel数据指标统计的滑动窗口算法 1.DegradeSlot实现熔断降级的原理与源码 (1)熔断降级规则DegradeRule的配置Demo (2)注册熔断降级监听器和加载熔断降级规则 (3)DegradeSlot根据熔断降级规则对请求进行验证 (1)熔断降级…...
C语言自增自减题目
一、题目引入 二、运行结果 三、题目分析 这一题中 i的初始值是2 所以执行case2中的命令i-- 表达式的值此时是2 i--完了之后i最后的值是1 由于是switch没有break 就会往下贯穿 直到遇到break为止 case3里面 i 表达式的值是2 i完了之后i的值也是2 综上所述 i的值最终的值是…...
paddleocr出现: [WinError 127] 找不到指定的程序解决办法
paddleocr是一个由百度开发开源的OCR(光学字符识别)工具库。它支持多种语言的文本识别,包括中文、英文、日文等,并具备高效的文本检测和识别能力。paddleocr基于PaddlePaddle深度学习框架开发,提供了丰富的预处理、模型…...
c++STL——list的使用和模拟实现
文章目录 list的使用和模拟实现使用部分list的结构声名默认成员函数initializer_list容量和访问操作修改操作其他接口list的迭代器迭代器的种类 list的模拟实现明确基本结构预处理函数迭代器部分(重点)思路进一步考虑最终代码operator->的重载总结 begin和end访问接口修改操…...
交换机端口安全
端口安全 端口安全(PortSecurity)通过将接口学习到的动态MAC地址转换为安全MAC地址(包括安全动态MAC、安全静态MAC和Sticky MAC),阻止非法用户通过本接口和交换机通信,从而增强设备的安全性。 1、安全mac地址分类 安全动态MAC地址…...
【Oracle专栏】Oracle中的虚拟列
Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 在EXP方式导出时,发现 出现如下提示 EXP-00107: virtual column 不支持,因此采用expdp方式导出。于是本文针对oracle虚拟列进行简单介绍。 2. 相…...
shell 正则表达式与文本处理器
目录 前言 一、正则表达式 (一)定义与用途 (二)基础正则表达式 (三)基础正则表达式元字符 (四)扩展正则表达式 二、文本处理器:Shell 编程的得力助手 ࿰…...
ZYNQ笔记(九):定时器中断
版本:Vivado2020.2(Vitis) 任务:使用定时器 (私有定时器) 中断 实现 LED(PS端) 定时1s亮灭翻转 目录 一、介绍 二、硬件设计 三、软件设计 四、效果 一、介绍 Zynq系列是Xilinx(现为AMD)推出的集成了AR…...
idea中运行groovy程序报错
我的项目是使用的 gradle 构建的。 在 idea 中运行Groovy的面向对象程序报错如下: Execution failed for task :Person.main(). > Process command G:/Program Files/jdk-17/jdk-17.0.12/bin/java.exe finished with non-zero exit value 1* Try: Run with --s…...
具身智能零碎知识点(四):联合嵌入预测架构(JEPAs)详解
联合嵌入预测架构(JEPAs)详解 联合嵌入预测架构(JEPAs)详解一、核心思想二、技术原理1. 核心组件2. 训练目标 三、与传统方法的对比四、具体实例例1:视频预测(如Meta的I-JEPA)例2:多…...
linux 搭建 dvwa 渗透测试环境
linux 安装 dvwa 1、分为4个部分,搭建dvwa渗透测试环境2、安装centos 7.63、安装apache http server4、安装mysql5、安装php6、运行dvwa 1、分为4个部分,搭建dvwa渗透测试环境 本文基于centos 7.6 搭建 dvwa 渗透测试环境 安装一个linux系统安装apache…...
C++项目 —— 基于多设计模式下的同步异步日志系统(4)(双缓冲区异步任务处理器(AsyncLooper)设计)
C项目 —— 基于多设计模式下的同步&异步日志系统(4)(双缓冲区异步任务处理器(AsyncLooper)设计) 异步线程什么是异步线程?C 异步线程简单例子代码解释程序输出关键点总结扩展:使…...
【Linux学习笔记】Linux的环境变量和命令行参数
【Linux学习笔记】Linux的环境变量和命令行参数 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】Linux的环境变量和命令行参数前言一.环境变量1.1基本概念1.2常见环境变量1.3和环境变量相关的命令1…...
排序算法-快速排序
描述: 基准值选择:选取数组的最后一个元素 arr[high] 作为基准值 p。初始化索引:i 初始化为 low - 1,其作用是指向比基准值小的最后一个元素的索引。遍历数组:借助 for 循环从 low 到 high - 1 遍历数组。若当前元素 …...
软考高级系统架构设计师-第16章 数学与经济管理
【本章学习建议】 根据考试大纲,本章主要考查系统架构设计师单选题,预计考2分左右。主要是运筹学的计算问题,范围广、难度大,超纲题较多,不用深究。 16.1 线性规划 线性规划是研究在有限的资源条件下,如果…...
爱在冰川-慢就是快
【游资大佬の搞钱心法🔥|小白逆袭必看冰川语录真实案例‼️】 💡刚扒完爱在冰川的万字访谈 发现游资搞钱真的靠"反人性思维" 总结6条狠人法则真实案例 建议收藏反复背诵👇 1️⃣【周期为王】💫 "行情…...
Mac-VScode-C++环境配置
mac上自带了clang所以不是必须下载Homebrew 下面是配置文件(注释记得删一下) package.json {"name": "git-base","displayName": "%displayName%","description": "%description%",&quo…...
【JAVA EE初阶】多线程(1)
这样的代码,虽然也能打印hello thread,但是没有创建新的线程,而是直接在main方法所在的主线程中执行了run的逻辑 start方法,是调用系统api,真正在操作系统内部创建一个线程。这个新的线程会以run作为入口方法ÿ…...
PHP伪协议读取文件
借鉴php伪协议实现命令执行,任意文件读取_ctf php文件读取-CSDN博客 总结 在ctf中常用的有data:// , php://input , php://filter ,file:// php://input ,data://用来执行命令 1.php://input 的用法 http://127.0.0.1/include.php?filephp://input [P…...
动态调整映射关系的一致性哈希负载均衡算法详解
一、核心原理与设计要点 双重映射结构 一致性哈希负载均衡通过 哈希环 和 槽动态分配 实现双重映射关系: • 哈希环构建:将节点(物理或虚拟)和数据键(Key)通过哈希函数(如MD5、CRC32)…...
控制反转(IOC)和依赖注入(DI)
Target Retention Documented 元注解 Component 将类交给IOC容器管理,成为IOC容器中的bean Autowired 注入运行时所需要依赖的对象 因为Mabatis DAO层注解Reponsitory 基本不用了,现在Mapper层Mapper注解,这里的Mapper层相当于原来的DAO层…...
【每日八股】复习 MySQL Day1:事务
文章目录 复习 MySQL Day1:事务MySQL 事务的四大特性?并发事务会出现什么问题?MySQL 事务的隔离级别?不同事务隔离级别下会发生什么问题?MVCC 的实现原理?核心数据结构版本链构建示例可见性判断算法MVCC 可…...
【数据结构和算法】1. 数据结构和算法简介、二分搜索
本文根据 数据结构和算法入门 视频记录 文章目录 1. 数据结构和算法简介1.1 什么是数据结构?什么是算法?1.2 数据结构和算法之间的关系1.3 “数据结构和算法”有那么重要吗? 2. 二分搜索(Binary Search)2.1 算法概念2…...
4月19日记(补)算了和周日一块写了 4月20日日记
周六啊 昨天晚上又玩的太嗨了。睡觉的时候有点晚了,眼睛疼就没写日记。现在补上 实际上现在是20号晚上八点半了。理论上来说应该写今天的日记。 周六上午打比赛啦,和研究生,输了,我是替补没上场。没关系再练一练明天就可以变强…...
面试常用基础算法
目录 快速排序归并排序堆排序 n n n皇后问题最大和子数组爬楼梯中心扩展法求最长回文子序列分割回文串动态规划求最长回文子序列最长回文子串单调栈双指针算法修改 分割回文串滑动窗口栈 快速排序 #include <iostream> #include <algorithm>using namespace std;…...
微服务与 SOA:架构异同全解析与应用指南
微服务和 SOA(面向服务的架构)是两种不同的软件架构风格,它们在很多方面存在相似之处,但也有一些区别。以下是对它们的详细介绍: 一、概念 1.微服务 微服务架构将一个大型应用程序拆分成多个小型、独立的服务&#…...
Dijkstra 算法入门笔记 (适用于算法竞赛初学者) - C++ 代码版
目录 算法是做什么的?核心思想:贪就完事了!算法前提:不能有负权边!需要哪些工具?(数据结构)算法具体步骤关键操作:松弛 (Relaxation)两种实现方式 (C 代码) 朴素版 Dijkstra (O(V^2))堆优化版 …...
脑影像分析软件推荐| GraphVar介绍
目录 1.软件界面 2.工具包功能简介 3.软件安装注意事项 1.软件界面 2.工具包功能简介 GraphVar是一个用户友好的 MATLAB 工具箱,用于对功能性大脑连接进行全面的图形分析。这里我们介绍了该工具箱的全面扩展,使用户能够无缝探索跨功能连接测量的可轻…...
如何优雅地实现全局唯一?深入理解单例模式
如何优雅地实现全局唯一?深入理解单例模式 一、什么是单例模式? 单例模式是一种创建型设计模式,旨在确保一个类只有一个实例,并为该实例提供全局访问点,从而避免全局变量的命名污染,并支持延迟初始化Wiki…...
【Flutter】使用LiveKit和Flutter构建实时视频聊天应用
引言 在当今快速发展的数字世界中,实时视频通信已成为许多应用程序的核心功能。无论是远程工作、在线教育还是社交网络,高质量的实时视频功能都至关重要。LiveKit作为一个开源的WebRTC解决方案,提供了构建可扩展实时音视频应用所需的一切工具…...
Android Jetpack Compose 状态管理解析:remember vs mutableStateOf,有啥不一样?为啥要一起用?
🌱《Jetpack Compose 状态管理解析:remember vs mutableStateOf,有啥不一样?为啥要一起用?》 在 Jetpack Compose 的世界里,UI 是响应式的。这意味着当状态发生变化时,UI 会自动重组࿰…...
QT6 源(37):界面组件的总基类 QWidget 的源码阅读(下,c++ 代码部分)
(1) QT 在 c 的基础上增加了自己的编译器,以支持元对象系统和 UI 界面设计,有 MOC 、 UIC 等 QT 自己的编译器。本节的源代码里,为了减少篇幅,易于阅读,去除了上篇中的属性部分, 上篇…...
进程与线程:01 CPU管理的直观想法
多进程图像与操作系统核心 好从今天开始,我们就要开始学习操作系统,最核心的图像是多进程图像。前面我们讲过,多进程图像对操作系统来说非常重要,它是操作系统的核心图像。明白了它以后,对于理解操作系统的一大部分内…...
19. git reflog
基本概述 git reflog 的作用是:查看本地仓库的引用日志(reference log),例如分支、HEAD等。它可以帮助你找回误删的提交、恢复被覆盖的分支,或回溯操作历史。 基本用法 1.查看完整的reflog git reflog这会显示所有…...
C语言 —— 铭纹织构未诞之镜 - 预处理详解
目录 1. 什么是预处理(预编译) 编辑 2. 预定义符号 3. #define 定义常量 4. #define定义宏 5. 带副作用的宏参数 6. 宏替换的规则 7. 宏和函数的对比 8. #和## 8.1 #运算符 8.2 ## 运算符 9. #undef 10. 条件编译 1. 什么是预处理…...
Linux 文件系统目录结构详解
Linux 文件系统目录结构详解 Linux 文件系统遵循 Filesystem Hierarchy Standard (FHS) 标准,定义了各个目录的用途和文件存放规则。无论是开发者、运维工程师还是普通用户,理解这些目录的作用都至关重要。本文将全面解析 Linux 的目录结构,…...
2025-4-19 情绪周期视角复盘(mini)
我本以为市场进化规律下产生龙头战法的末法时代导致情绪周期逐步混乱或者说混沌期漫长。所谓的市场进化无非也是量化的发展和各类资金逐步量化化的充分博弈下的结果。通过逐步向上思考发现,不仅仅我们的市场是处于一个存量的时代背景,重要的是我们的思维…...
-实用类-
1. API是什么 2.什么是枚举 !有点类似封装! 2.包装类 注意: 1.Boolean类构造方法参数为String类型时,若该字符串内容为true(不考虑大小写),则该Boolean对象表示true,否则表示false 2.当包装类构造方法参…...
Unity3D仿星露谷物语开发36之锄地动画2
1、目标 当角色锄地之后,地面会显示开垦后的样貌。 2、思路 上一篇中,虽然角色dig了hoe,同时grid属性也改变了,但是没有任何可视化的反馈。我们现在将添加新的功能,动态地将"dug ground"瓷砖添加到"…...
【备考高项】模拟预测题(一)案例分析及答案详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 试题一【问题 1】(10分)【问题 2】(5分)【问题 3】(4分)【问题 4】(6分)试题二【问题 1】(12分)【问题 2】(3分)【问题 3】(6分)【问题 4】(4分)试题三【问题 1】(4分)【问题 2】(10分)【问题 3】…...
7、sentinel
控制台访问地址:http://localhost:8080/ 依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>配置文件 spring:cloud:sentinel:transpo…...
状态管理最佳实践:Provider使用技巧与源码分析
状态管理最佳实践:Provider使用技巧与源码分析 前言 Provider是Flutter官方推荐的状态管理解决方案,它简单易用且功能强大。本文将从实战角度深入讲解Provider的使用技巧和源码实现原理,帮助你更好地在项目中应用Provider进行状态管理。 基…...
INFINI Console 系统集群状态异常修复方案
背景介绍 运行 INFINI Console 1.29.0 和 1.29.1 版本 的用户在 新初始化 平台后可能会遇到一个特定问题。如果后台的系统 Easysearch/Elasticsearch 集群(存储 Console 元数据的集群,通常名为 .infini_cluster 或类似名称)包含超过一个节点…...
Spring Boot自动装配原理(源码详细剖析!)
什么是Spring Boot的自动装配? 自动装配是Spring Boot的核心功能,它能够根据应用程序的依赖和配置自动配置Spring。这意味着我们只需要添加大量的依赖,Spring Boot就能自动完成配置,减少了人工配置的工作量。 自动装配的核心注…...
大数据驱动的高效能量管理:智能优化与实践探索
大数据驱动的高效能量管理:智能优化与实践探索 在全球能源需求不断增长的背景下,如何提高能源利用效率成为各行业关注的焦点。传统的能源管理方式往往依赖固定规则和人工监测,难以适应复杂多变的应用场景。而大数据技术的兴起,为能量管理提供了新的解决方案——通过数据驱…...
《银行数字化风控-业务于实战》读后知识总结
引言 在金融科技高速发展的今天,银行的风控体系正经历从“人工经验驱动”向“数据智能驱动”的深刻变革。《银行数字化风控-业务于实战》一书以实战为导向,系统性地剖析了数字化风控的核心逻辑、技术实现路径及业务落地方法论。作为深耕风控领域多年的从…...
初级达梦dba的技能水准
在x86环境(windows、linux)安装单机软件,安装客户端创建过至少20套数据库,优化参数并更新过正式许可会用逻辑导出导入以及dmrman备份了解manager工具的使用配置sqllog日志,并能解释输出内容能够分析因磁盘空间不足、内…...
C++初阶-类和对象(中)
目录 1.类的默认成员函数 2.构造函数(难度较高) 编辑 编辑 编辑 3.析构函数 4.拷贝构造函数 5.赋值运算符重载 5.1运算符重载 5.2赋值运算符重载 6.取地址运算符重载 6.1const成员函数 6.2取地址运算符重载 7.总结 1.类的默认成员函数…...
Linux网络UDP与TCP
基础知识 传输层 负责数据能够从发送端传输接收端。 端口号(Port)标识了一个主机上进行通信的不同的应用程序; 在 TCP/IP 协议中, 用 “源 IP”, “源端口号”, “目的 IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信(可以通过 netstat -n 查看); 端口号范…...
23、.NET和C#有什么区别?
1、定义与范畴 .NET 定义 .NET 是一个由微软开发的开发平台(Platform),它提供了一套完整的工具、库和运行时环境,用于构建各种类型的应用程序。 范畴 包括 .NET Framework、.NET Core(现称为 .NET 5 及以上版本&a…...