当前位置: 首页 > news >正文

Sentinel源码—8.限流算法和设计模式总结二

大纲

1.关于限流的概述

2.高并发下的四大限流算法原理及实现

3.Sentinel使用的设计模式总结

3.Sentinel使用的设计模式总结

(1)责任链模式

(2)监听器模式

(3)适配器模式

(4)模版方法模式

(5)策略模式

(6)观察者模式

(1)责任链模式

一.责任链接口ProcessorSlot

二.责任链接口的抽象实现类

三.责任链的构建

Sentinel的功能都是靠一条链式的ProcessorSlot来完成的,这些ProcessorSlot的初始化以及调用便使用了责任链模式。

一.责任链接口ProcessorSlot

entry()方法相当于AOP的before()方法,也就是入口方法,因此责任链执行时会调用entry()方法。

exit()方法相当于AOP的after()方法,也就是出口方法,因此责任链执行结束时会调用exit()方法。

fireEntry()方法相当于AOP在执行完before()方法后调用pjp.proceed()方法,也就是调用责任链上的下一个节点的entry()方法。

fireExit()方法相当于AOP在执行完exit()方法后调用pjp.proceed()方法,也就是调用责任链上的下一个节点的exit()方法。

//A container of some process and ways of notification when the process is finished.
public interface ProcessorSlot<T> {//Entrance of this slot.//@param context         current Context//@param resourceWrapper current resource//@param param           generics parameter, usually is a com.alibaba.csp.sentinel.node.Node//@param count           tokens needed//@param prioritized     whether the entry is prioritized//@param args            parameters of the original call//@throws Throwable blocked exception or unexpected errorvoid entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized, Object... args) throws Throwable;//Means finish of #entry(Context, ResourceWrapper, Object, int, boolean, Object...).//@param context         current Context//@param resourceWrapper current resource//@param obj             relevant object (e.g. Node)//@param count           tokens needed//@param prioritized     whether the entry is prioritized//@param args            parameters of the original call//@throws Throwable blocked exception or unexpected errorvoid fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable;//Exit of this slot.//@param context         current Context//@param resourceWrapper current resource//@param count           tokens needed//@param args            parameters of the original callvoid exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);//Means finish of #exit(Context, ResourceWrapper, int, Object...).//@param context         current Context//@param resourceWrapper current resource//@param count           tokens needed//@param args            parameters of the original callvoid fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}

二.责任链接口的抽象实现类

public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {//下一个节点,这里的责任链是一个单向链表,因此next就是当前节点所指向的下一个节点private AbstractLinkedProcessorSlot<?> next = null;//触发执行责任链下一个节点的entry()方法@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable {if (next != null) {next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}@SuppressWarnings("unchecked")void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args) throws Throwable {T t = (T)o;entry(context, resourceWrapper, t, count, prioritized, args);}//触发执行责任链下一个节点的exit()方法@Overridepublic void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {if (next != null) {next.exit(context, resourceWrapper, count, args);}}public AbstractLinkedProcessorSlot<?> getNext() {return next;}public void setNext(AbstractLinkedProcessorSlot<?> next) {this.next = next;}
}

三.责任链的构建

Sentinel在默认情况下会通过DefaultProcessorSlotChain类来实现责任链的构建,当然我们也可以通过SPI机制指定一个自定义的责任链构建类。

//Builder for a default {@link ProcessorSlotChain}.
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {@Overridepublic ProcessorSlotChain build() {//创建一个DefaultProcessorSlotChain对象实例ProcessorSlotChain chain = new DefaultProcessorSlotChain();//通过SPI机制加载责任链的节点ProcessorSlot实现类//然后按照@Spi注解的order属性进行排序并进行实例化//最后将ProcessorSlot实例放到sortedSlotList中List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();//遍历已排好序的ProcessorSlot集合for (ProcessorSlot slot : sortedSlotList) {//安全检查,防止业务系统也写了一个SPI文件,但没按规定继承AbstractLinkedProcessorSlotif (!(slot instanceof AbstractLinkedProcessorSlot)) {RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");continue;}//调用DefaultProcessorSlotChain.addLast()方法构建单向链表//将责任链的节点ProcessorSlot实例放入DefaultProcessorSlotChain中chain.addLast((AbstractLinkedProcessorSlot<?>) slot);}//返回单向链表return chain;}
}

DefaultProcessorSlotChain构建的责任链如下:

(2)监听器模式

一.监听器接口和具体实现

二.监听器管理器接口和具体实现

三.使用方如何基于这套监听器机制管理规则

Sentinel在加载和配置规则的时候就使用了监听器模式。监听器模式的实现分为三大部分:监听器、监听器管理器、使用方(比如规则管理器)。

一.监听器接口和具体实现

//This class holds callback method when SentinelProperty#updateValue(Object) need inform the listener
//监听器接口,负责监听各个配置,包含两个方法:初始化方法以及更新方法
public interface PropertyListener<T> {//Callback method when SentinelProperty#updateValue(Object) need inform the listener.//规则变更时触发的回调方法void configUpdate(T value);//The first time of the value's load.//首次加载规则时触发的回调方法void configLoad(T value);
}//流控规则管理器
public class FlowRuleManager {...//监听器接口的具体实现:流控规则监听器private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {//初始化规则@Overridepublic synchronized void configUpdate(List<FlowRule> value) {Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);if (rules != null) {flowRules = rules;}RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);}//规则变更@Overridepublic synchronized void configLoad(List<FlowRule> conf) {Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);if (rules != null) {flowRules = rules;}RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);}}
}

二.监听器管理器接口和具体实现

//监听器管理器接口
public interface SentinelProperty<T> {//添加监听者void addListener(PropertyListener<T> listener);//移除监听者void removeListener(PropertyListener<T> listener);//当监听值有变化时,调用此方法进行通知boolean updateValue(T newValue);
}//监听器管理器具体实现
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {//存放每个监听器protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();//要监听的值private T value = null;public DynamicSentinelProperty() {}//添加监听器到集合@Overridepublic void addListener(PropertyListener<T> listener) {listeners.add(listener);//回调监听器的configLoad()方法初始化规则配置listener.configLoad(value);}//移除监听器@Overridepublic void removeListener(PropertyListener<T> listener) {listeners.remove(listener);}//更新值@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;}//对比值是否发生了变化private boolean isEqual(T oldValue, T newValue) {if (oldValue == null && newValue == null) {return true;}if (oldValue == null) {return false;}return oldValue.equals(newValue);}//清空监听器集合public void close() {listeners.clear();}
}

三.使用方如何基于这套监听器机制管理规则

//流控规则管理器
public class FlowRuleManager {//维护每个资源的流控规则列表,key是资源名称,value是资源对应的规则private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();//饿汉式单例模式实例化流控规则的监听器对象private static final FlowPropertyListener LISTENER = new FlowPropertyListener();//监听器对象的管理器private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();//当FlowRuleManager类的静态方法首次被调用时,会执行这里的静态代码块(对应类加载的过程)static {//将流控规则监听器注册到监听器管理器中currentProperty.addListener(LISTENER);startMetricTimerListener();}//Load FlowRules, former rules will be replaced.//加载流控规则public static void loadRules(List<FlowRule> rules) {//通知监听器管理器中的每一个监听器,规则已发生变化,需要重新加载规则配置//其实就是更新FlowRuleManager规则管理器中的流控规则列表flowRulescurrentProperty.updateValue(rules);}...
}//使用方:通过流控规则管理器FlowRuleManager加载和监听流控规则
public class FlowQpsDemo {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 = 32;private static int seconds = 60 + 40;public static void main(String[] args) throws Exception {//初始化QPS的流控规则initFlowQpsRule();//启动线程定时输出信息tick();//first make the system run on a very low condition//模拟QPS为32时的访问场景simulateTraffic();System.out.println("===== begin to do flow control");System.out.println("only 20 requests per second can pass");}private static void initFlowQpsRule() {List<FlowRule> rules = new ArrayList<FlowRule>();FlowRule rule1 = new FlowRule();rule1.setResource(KEY);//设置QPS的限制为20rule1.setCount(20);rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);rule1.setLimitApp("default");rules.add(rule1);//首次调用FlowRuleManager的静态方法会加载FlowRuleManager类执行其静态代码块//加载流控规则FlowRuleManager.loadRules(rules);}private static void simulateTraffic() {for (int i = 0; i < threadCount; i++) {Thread t = new Thread(new RunTask());t.setName("simulate-traffic-Task");t.start();}}private static void tick() {Thread timer = new Thread(new TimerTask());timer.setName("sentinel-timer-task");timer.start();}static class TimerTask implements Runnable {@Overridepublic 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(seconds + " send qps is: " + oneSecondTotal);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);}}static class RunTask implements Runnable {@Overridepublic void run() {while (!stop) {Entry entry = null;try {//调用entry()方法开始规则验证entry = SphU.entry(KEY);//token acquired, means passpass.addAndGet(1);} catch (BlockException e1) {block.incrementAndGet();} catch (Exception e2) {//biz exception} finally {total.incrementAndGet();if (entry != null) {//完成规则验证调用exit()方法entry.exit();}}Random random2 = new Random();try {TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));} catch (InterruptedException e) {// ignore}}}}
}

(3)适配器模式

适配器模式是一种结构型设计模式,它允许将一个类的接口转换为客户端期望的另一个接口。在Sentinel中,使用适配器模式将不同框架和库的接口适配为统一的接口,如SphU类。SphU类提供了统一的入口,用于执行不同的资源保护逻辑。

public class SphU {private static final Object[] OBJECTS0 = new Object[0];private SphU() {            }//Record statistics and perform rule checking for the given resource.//@param name the unique name of the protected resource//@return the Entry of this invocation (used for mark the invocation complete and get context data)public static Entry entry(String name) throws BlockException {//调用CtSph.entry()方法创建一个Entry资源访问对象return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);}//Checking all Rules about the protected method.//@param method the protected methodpublic static Entry entry(Method method) throws BlockException {return Env.sph.entry(method, EntryType.OUT, 1, OBJECTS0);}//Checking all Rules about the protected method.//@param method     the protected method//@param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)public static Entry entry(Method method, int batchCount) throws BlockException {return Env.sph.entry(method, EntryType.OUT, batchCount, OBJECTS0);}//Record statistics and perform rule checking for the given resource.//@param name       the unique string for the resource//@param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)public static Entry entry(String name, int batchCount) throws BlockException {return Env.sph.entry(name, EntryType.OUT, batchCount, OBJECTS0);}//Checking all Rules about the protected method.//@param method      the protected method//@param trafficType the traffic type (inbound, outbound or internal). //This is used to mark whether it can be blocked when the system is unstable, only inbound traffic could be blocked by SystemRule.public static Entry entry(Method method, EntryType trafficType) throws BlockException {return Env.sph.entry(method, trafficType, 1, OBJECTS0);}//Record statistics and perform rule checking for the given resource.public static Entry entry(String name, EntryType trafficType) throws BlockException {//调用CtSph.entry()方法创建一个Entry资源访问对象return Env.sph.entry(name, trafficType, 1, OBJECTS0);}//Checking all Rules about the protected method.//@param method      the protected method//@param trafficType the traffic type (inbound, outbound or internal). //This is used to mark whether it can be blocked when the system is unstable, only inbound traffic could be blocked by SystemRule.//@param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)public static Entry entry(Method method, EntryType trafficType, int batchCount) throws BlockException {return Env.sph.entry(method, trafficType, batchCount, OBJECTS0);}//Record statistics and perform rule checking for the given resource.public static Entry entry(String name, EntryType trafficType, int batchCount) throws BlockException {return Env.sph.entry(name, trafficType, batchCount, OBJECTS0);}//Checking all Rules about the protected method.//@param method      the protected method//@param trafficType the traffic type (inbound, outbound or internal). //This is used to mark whether it can be blocked when the system is unstable, only inbound traffic could be blocked by SystemRule.//@param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)//@param args        args for parameter flow control or customized slots//@return the Entry of this invocation (used for mark the invocation complete and get context data)public static Entry entry(Method method, EntryType trafficType, int batchCount, Object... args) throws BlockException {return Env.sph.entry(method, trafficType, batchCount, args);}//Record statistics and perform rule checking for the given resource.public static Entry entry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException {return Env.sph.entry(name, trafficType, batchCount, args);}//Record statistics and check all rules of the resource that indicates an async invocation.//@param name the unique name of the protected resourcepublic static AsyncEntry asyncEntry(String name) throws BlockException {return Env.sph.asyncEntry(name, EntryType.OUT, 1, OBJECTS0);}//Record statistics and check all rules of the resource that indicates an async invocation.//@param name        the unique name for the protected resource//@param trafficType the traffic type (inbound, outbound or internal). //This is used to mark whether it can be blocked when the system is unstable, only inbound traffic could be blocked by SystemRule.//@return the Entry of this invocation (used for mark the invocation complete and get context data)public static AsyncEntry asyncEntry(String name, EntryType trafficType) throws BlockException {return Env.sph.asyncEntry(name, trafficType, 1, OBJECTS0);}public static AsyncEntry asyncEntry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException {return Env.sph.asyncEntry(name, trafficType, batchCount, args);}//Record statistics and perform rule checking for the given resource. The entry is prioritized.public static Entry entryWithPriority(String name) throws BlockException {return Env.sph.entryWithPriority(name, EntryType.OUT, 1, true);}//Record statistics and perform rule checking for the given resource. The entry is prioritized.public static Entry entryWithPriority(String name, EntryType trafficType) throws BlockException {return Env.sph.entryWithPriority(name, trafficType, 1, true);}//Record statistics and perform rule checking for the given resource.public static Entry entry(String name, int resourceType, EntryType trafficType) throws BlockException {return Env.sph.entryWithType(name, resourceType, trafficType, 1, OBJECTS0);}//Record statistics and perform rule checking for the given resource.public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args) throws BlockException {return Env.sph.entryWithType(name, resourceType, trafficType, 1, args);}//Record statistics and perform rule checking for the given resource that indicates an async invocation.public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType) throws BlockException {return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, OBJECTS0);}//Record statistics and perform rule checking for the given resource that indicates an async invocation.public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, Object[] args) throws BlockException {return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, args);}//Record statistics and perform rule checking for the given resource that indicates an async invocation.public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, int batchCount, Object[] args) throws BlockException {return Env.sph.asyncEntryWithType(name, resourceType, trafficType, batchCount, false, args);}
}

(4)模版方法模式

模板方法模式是一种行为型设计模式,它在一个方法中定义一个算法的骨架,将一些步骤延迟到子类中实现。Sentinel便使用了类似模板方法模式来处理熔断策略,但不是严格意义上的模板模式,因为模板方法模式一般会有一个final修饰的模板方法来定义整个流程。例如AbstractCircuitBreaker类定义了熔断策略的基本结构,具体的细节需要继承它并实现对应的方法。

public abstract class AbstractCircuitBreaker implements CircuitBreaker {@Overridepublic boolean tryPass(Context context) {...}//提供抽象方法供子类实现abstract void resetStat();
}//子类
public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {@Overrideprotected void resetStat() {stat.currentWindow().value().reset();}
}

(5)策略模式

策略模式是一种行为型设计模式,定义了一系列的算法,并将每个算法封装起来,使它们可以互相替换。Sentinel便在构建流控规则对象时使用了策略模式来设置不同的流控策略。例如TrafficShapingController接口定义了流控策略的方法,具体的实现类负责实现不同的流控策略。

//流控效果接口
public interface TrafficShapingController {//Check whether given resource entry can pass with provided count.//@param node resource node//@param acquireCount count to acquire//@param prioritized whether the request is prioritized//@return true if the resource entry can pass; false if it should be blockedboolean canPass(Node node, int acquireCount, boolean prioritized);//Check whether given resource entry can pass with provided count.//@param node resource node//@param acquireCount count to acquire//@return true if the resource entry can pass; false if it should be blockedboolean canPass(Node node, int acquireCount);
}//流控规则管理器
public class FlowRuleManager {...private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {//初始化规则@Overridepublic synchronized void configUpdate(List<FlowRule> value) {//构建流控规则对象Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);if (rules != null) {flowRules = rules;}RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);}//规则变更@Overridepublic synchronized void configLoad(List<FlowRule> conf) {//构建流控规则对象Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);if (rules != null) {flowRules = rules;}RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);}}
}public final class FlowRuleUtil {...public static <K> Map<K, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction, Predicate<FlowRule> filter, boolean shouldSort) {Map<K, List<FlowRule>> newRuleMap = new ConcurrentHashMap<>();if (list == null || list.isEmpty()) {return newRuleMap;}Map<K, Set<FlowRule>> tmpMap = new ConcurrentHashMap<>();for (FlowRule rule : list) {if (!isValidRule(rule)) {RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);continue;}if (filter != null && !filter.test(rule)) {continue;}if (StringUtil.isBlank(rule.getLimitApp())) {rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);}//获取[流控效果]处理器TrafficShapingController rater = generateRater(rule);rule.setRater(rater);//获取资源名K key = groupFunction.apply(rule);if (key == null) {continue;}//获取资源名对应的流控规则列表Set<FlowRule> flowRules = tmpMap.get(key);//将规则放到Map里,和当前资源绑定if (flowRules == null) {//Use hash set here to remove duplicate rules.flowRules = new HashSet<>();tmpMap.put(key, flowRules);}flowRules.add(rule);}Comparator<FlowRule> comparator = new FlowRuleComparator();for (Entry<K, Set<FlowRule>> entries : tmpMap.entrySet()) {List<FlowRule> rules = new ArrayList<>(entries.getValue());if (shouldSort) {//Sort the rules.Collections.sort(rules, comparator);}newRuleMap.put(entries.getKey(), rules);}return newRuleMap;}private static TrafficShapingController generateRater(FlowRule rule) {//判断只有当阈值类型为QPS时才生效if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {//根据流控效果选择不同的流量整形控制器TrafficShapingControllerswitch (rule.getControlBehavior()) {case RuleConstant.CONTROL_BEHAVIOR_WARM_UP://Warm Up预热模式——冷启动模式return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(), ColdFactorProperty.coldFactor);case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER://排队等待模式return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER://Warm Up + 排队等待模式return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(), rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);case RuleConstant.CONTROL_BEHAVIOR_DEFAULT://快速失败模式——Default默认模式default://Default mode or unknown mode: default traffic shaping controller (fast-reject).}}//默认模式:快速失败用的是DefaultControllerreturn new DefaultController(rule.getCount(), rule.getGrade());}...
}public class FlowRuleChecker {...private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {//选择Node作为限流计算的依据Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);if (selectedNode == null) {return true;}//先通过FlowRule.getRater()方法获取流控规则对应的流量整形控制器//然后调用TrafficShapingController.canPass()方法对请求进行检查return rule.getRater().canPass(selectedNode, acquireCount, prioritized);}...
}

(6)观察者模式

Sentinel实现熔断功能使用了观察者模式。具体接口是CircuitBreakerStateChangeObserver,它负责感知熔断器状态发生变化后通知到各个观察者。

//1.首先定义观察者接口CircuitBreakerStateChangeObserver
public interface CircuitBreakerStateChangeObserver {void onStateChange(CircuitBreaker oldCircuitBreaker, CircuitBreaker newCircuitBreaker);
}//2.在熔断器事件注册类EventObserverRegistry中:
//定义一个观察者Map(stateChangeObserverMap)用于存放观察者实例,并提供注册、移除和获取全部观察者的方法
public class EventObserverRegistry {private final Map<String, CircuitBreakerStateChangeObserver> stateChangeObserverMap = new HashMap<>();//注册观察者public void addStateChangeObserver(String name, CircuitBreakerStateChangeObserver observer) {stateChangeObserverMap.put(name, observer);}//移除观察者public boolean removeStateChangeObserver(String name) {return stateChangeObserverMap.remove(name) != null;}//获取全部观察者public List<CircuitBreakerStateChangeObserver> getStateChangeObservers() {return new ArrayList<>(stateChangeObserverMap.values());}...
}//3.当熔断器状态发生变化时,通知所有已注册的观察者。
//比如在AbstractCircuitBreaker类中的notifyObservers方法中实现:
public abstract class AbstractCircuitBreaker implements CircuitBreaker {private void notifyObservers(CircuitBreaker.State prevState, CircuitBreaker.State newState, Double snapshotValue) {for (CircuitBreakerStateChangeObserver observer : observerRegistry.getStateChangeObservers()) {observer.onStateChange(prevState, newState, rule, snapshotValue);}}
}

相关文章:

Sentinel源码—8.限流算法和设计模式总结二

大纲 1.关于限流的概述 2.高并发下的四大限流算法原理及实现 3.Sentinel使用的设计模式总结 3.Sentinel使用的设计模式总结 (1)责任链模式 (2)监听器模式 (3)适配器模式 (4)模版方法模式 (5)策略模式 (6)观察者模式 (1)责任链模式 一.责任链接口ProcessorSlot 二.责…...

Docker 部署 MySQL 数据库

Docker 部署 MySQL 数据库 基于 Docker 部署 MySQL 数据库一、拉取 MySQL 镜像二、运行 MySQL 容器三、运行命令参数详解四、查看容器运行状态 基于 Docker 部署 MySQL 数据库 一、拉取 MySQL 镜像 在开始之前&#xff0c;请确保你的 Docker 环境已经正确安装并可以正常运行。…...

【Linux运维涉及的基础命令与排查方法大全】

文章目录 前言1、计算机网络常用端口2、Kali Linux中常用的命令3、Kali Linux工具的介绍4、Ubuntu没有网络连接解决方法5、获取路由6、数据库端口 前言 以下介绍计算机常见的端口已经对应的网络协议&#xff0c;Linux中常用命令&#xff0c;以及平时运维中使用的排查网络故障的…...

映射(Mapping)和地址(Address)

Addresses &#xff08;地址&#xff09; 以太坊区块链由 _ account _ (账户)组成&#xff0c;你可以把它想象成银行账户。一个帐户的余额是 以太 &#xff08;在以太坊区块链上使用的币种&#xff09;&#xff0c;你可以和其他帐户之间支付和接受以太币&#xff0c;就像你的银…...

用Java实现简易区块链:从零开始的探索

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4o-mini模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 区块链技术作为近年来的热门话题&am…...

Spark-Streaming

Spark-Streaming 一、Spark-Streaming简介 1、Spark-Streaming概述 1.1、Spark-Streaming是什么 Spark Streaming 用于流式数据的处理。Spark Streaming 支持的数据输入源很多&#xff0c;例如&#xff1a;Kafka、Flume、Twitter等&#xff0c;以及和简单的 TCP 套接字等等…...

工程投标k值分析系统(基于微服务实现)

1 需求总括 2 企业管理模块: 新增、删除、修改企业/部门 <...

WebGL 工作原理

WebGL在GPU上的工作基本上分为两部分 第一部分是将顶点&#xff08;或数据流&#xff09;转换到裁剪空间坐标 就是将传入的位置坐标&#xff0c;转换为0-1的值&#xff0c;并绘制出来每个顶点的坐标&#xff08;传入的值&#xff09;通过顶点着色器计算转换为裁剪空间坐标转换…...

【 React 】重点知识总结 快速上手指南

react 是 facebook 出的一款针对视图层的库。react 使用的是单向数据流的机制 React 官方中文文档 基础 api 和语法 jsx 语法 就是在 js 中插入 html 片段 在 React 中所有的组件都是 function 组件定义 function 定义组件 就是使用 function 定义组件 任何一个 function …...

Docker 部署 Redis 缓存服务

Docker 部署 Redis 缓存服务 基于 Docker 部署 Redis 缓存服务一、拉取 Redis 镜像二、运行 Redis 容器三、运行命令参数详解四、查看容器运行状态 基于 Docker 部署 Redis 缓存服务 一、拉取 Redis 镜像 确保 Docker 环境已正确安装并运行&#xff0c;打开终端执行以下命令拉…...

A2A + MCP:构建实用人工智能系统的超强组合

构建真正有效的连接型人工智能系统的挑战 如果你正在构建人工智能应用&#xff0c;这种情况可能听起来很熟悉&#xff1a; 你需要特定的人工智能能力来解决业务问题。你找到了完成每个单独任务的出色工具。但把所有东西连接在一起却占据了大部分开发时间&#xff0c;还创建了…...

全能 Sui 技术栈,构建 Web3 的未来

本文翻译自&#xff1a;FourPillarsFP&#xff0c;文章仅代表作者观点。 2025 年&#xff0c;SuiNetwork正在以一套全栈区块链策略强势出击&#xff0c;彻底打破加密行业的传统范式。正如 Mysten Labs 联合创始人 Adeniyi Abiodun 所说&#xff1a;“Sui 不只是一条区块链&…...

企业微信私域运营,基于http协议实现SCRM+AI完整解决方案

1、方案介绍 基于企业微信原生功能已实现全场景的能力覆盖&#xff0c;并提供标准化可直接调用的API接口&#xff0c;可以帮助企业轻松实现上层应用的开发及落地&#xff0c;方案采用模拟通信技术可实现PC&#xff0c;手机&#xff0c;ipad三端的同时在线&#xff0c;单服务器…...

【C++】Json-Rpc框架项目介绍(1)

项目介绍 RPC&#xff08;Remote Procedure Call&#xff09;即远程过程调用&#xff0c;是一种通过网络从远程计算机程序中请求服务而不需要了解底层网络实现细节的一种 协议 。 RPC&#xff08;Remote Procedure Call&#xff09;可以使用多种网络协议进行通信&#xff0c;如…...

数据结构图论基础知识(一)

文章目录 1. 图的基本概念2. 图的一些现实的应用2.1 ABCDE各个城市之间的关系2.2 社交关系 3. 图的存储结构3.1邻接矩阵3.2 邻接矩阵的实现3.3 邻接表 1. 图的基本概念 1. &#xff08;graph&#xff09;图由边&#xff08;edge&#xff09;和顶点&#xff08;Vertex&#xff…...

安宝特案例 | AR如何大幅提升IC封装厂检测效率?

前言&#xff1a;如何提升IC封装厂检测效率&#xff1f; 在现代电子产品的制造过程中&#xff0c;IC封装作为核心环节&#xff0c;涉及到复杂处理流程和严格质量检测。这是一家专注于IC封装的厂商&#xff0c;负责将来自IC制造商的晶圆进行保护、散热和导通处理。整个制程繁琐…...

2024年ESWA SCI1区TOP:量子计算蜣螂算法QHDBO,深度解析+性能实测

目录 1.摘要2.蜣螂优化算法DBO原理3.改进策略4.结果展示5.参考文献6.代码获取 1.摘要 蜣螂优化算法是一种群体智能优化算法&#xff0c;具有较强的优化能力和快速收敛性&#xff0c;但容易在优化过程后期陷入局部最优解。本文提出了一种量子计算和多策略混合的蜣螂优化算法&am…...

数据结构*链表- LinkedList

什么是链表 相较于ArrayList顺序表来说&#xff0c;链表是物理存储结构上非连续存储结构&#xff08;也就是地址不是连续的&#xff09;。链表由一系列节点构成&#xff0c;每个节点包含数据和指向下一个节点的引用。链表的各个节点在内存里并非连续存储&#xff0c;而是通过引…...

WebRTC服务器Coturn服务器用户管理和安全性

1、概述 Coturn服务器对用户管理和安全方面也做了很多的措施&#xff0c;以下会介绍到用户方面的设置 1.1、相关术语 1.1.1 realm 在 coturn 服务器中&#xff0c;域&#xff08;realm&#xff09;是一种逻辑上的分组概念&#xff0c;用于对不同的用户群体、应用或者服务进行区…...

MySQL聚簇索引和非聚簇索引

聚簇索引&#xff08;Clustered Index&#xff09;和非聚簇索引&#xff08;Non-Clustered Index&#xff09;是数据库中常用的两种索引类型&#xff0c;它们在数据存储和检索方式上有显著的区别。 一、聚簇索引&#xff08;Clustered Index&#xff09; 聚簇索引是数据表中的…...

QT加入阿里云OSS上传图片SDK,编译报错:error LNK2019: 无法解析的外部符号 EVP_MD_CTX_init

参考此链接把OSS上传图片的SDK&#xff0c;cmake成lib库&#xff0c;如下图 将lib库放入自己的项目文件夹下&#xff0c;并在pro文件中添加此lib库。而解决 “无法解析的外部符号 EVP_MD_CTX_init” 问题&#xff0c;则需要将third_party文件夹下的一些依赖库和头文件都放到自己…...

正点原子TFTLCD扩展

声明&#xff1a;该文章代码是在正点原子教学下写出的LCD驱动代码上进行了修改能兼容更多字号( ˘ ˘)❤️ 如有侵权&#xff0c;请私信联系删除 文章目录 前言代码lcd.clcd.hfont.h 前言 由于TFTLCD4.3寸屏幕太大了&#xff0c;正点原子的代码只能显示12/16/24字号的字符或者…...

部署Megatron - LM,快速上手使用

安装Megatron - LM 首先检查一下当前环境是否已经有 NVIDIA 驱动和 CUDA&#xff1a; nvidia-smi 直接在当前环境安装运行 PyTorch 和 Megatron-LM不使用 Docker 之前我们看到目前的环境有 NVIDIA V100 GPU 和 CUDA 12.1&#xff0c;我们可以安装对应的 GPU 版本 PyTorch。…...

赛灵思 XC7K325T-2FFG900I FPGA Xilinx Kintex‑7

XC7K325T-2FFG900I 是 Xilinx Kintex‑7 系列中一款工业级 (I) 高性能 FPGA&#xff0c;基于 28 nm HKMG HPL 工艺制程&#xff0c;核心电压标称 1.0 V&#xff0c;I/O 电压可在 0.97 V–1.03 V 之间灵活配置&#xff0c;并可在 –40 C 至 100 C 温度范围内稳定运行。该器件提供…...

20.1Linux的PWM驱动实验(知识点)_csdn

PWM 是很常用到功能&#xff0c;我们可以通过 PWM 来控制电机速度&#xff0c;也可以使用 PWM 来控制LCD 的背光亮度。本章我们就来学习一下如何在 Linux 下进行 PWM 驱动开发。 在之前学过STM32的就知道这部分内容&#xff0c;利用占空比来调节电机的转速。 1、PWM 驱动简析 …...

如何在 Java 中从 PDF 文件中删除页面(教程)

由于 PDF 文件格式不是 Java 原生支持的&#xff0c;因此要从 PDF 中删除页面&#xff0c;你需要使用外部库。 本教程介绍如何使用 JPedal 来实现这一功能。 开始使用 • 将 JPedal 添加到你的类路径或模块路径中&#xff08;可从官网下载安装试用版 JAR 文件&#xff09; •…...

2026《数据结构》考研复习笔记五(栈、队列)

栈、队列 一、栈1.卡特兰数2.不合法的出栈序列 二、队列1.循环队列2.输入输出受限队列&#xff08;四个数1234&#xff09; 三、算法1.栈在括号匹配中的应用2.中缀表达式求值&#xff08;通过转化为后缀表达式再后缀表达式求值&#xff09;3.中缀表达式转化为后缀表达式4.后缀表…...

本地部署DeepSeek大模型

本地部署 ollama 下载Ollama ollama支持的模型&#xff1a;Ollama Search 直接下载&#xff0c;发现默认是下载C盘&#xff0c;并且不能选择安装目录&#xff0c;这对我C盘的压力太大了。 查阅资料&#xff1a;发现可以修改 参考将Ollama安装到非C盘路径_ollama不安装在c盘…...

C++ / 引用 | 类

引用本 作用&#xff1a; 给变量起别名 语法&#xff1a; 数据类型 & 别名 原名&#xff1b; 应用代码 #include <iostream>using namespace std;int main() {int a 10;int& b a;b 100;cout << "a " << a << endl;cout <…...

在任意路径下简单开启jupyter notebook

正常的开启方式为&#xff1a; 安装anaconda, 在anaconda界面开启jupyter. 但是启动后root的路径不好改变。 可以直接通过cmd方式打开jupyter. cd D: # cd到d盘 jupyter notebook # 启动此时开启jupyter notebook, root为D盘路径。 按此方式可以在任意路径启动jupyter noteb…...

2025年阿里云云计算ACP高级工程师认证模拟试题(附答案解析)

这篇文章的内容是阿里云云计算ACP高级工程师认证考试的模拟试题。 所有模拟试题由AI自动生成&#xff0c;主要为了练习和巩固知识&#xff0c;并非所谓的 “题库”&#xff0c;考试中如果出现同样试题那真是纯属巧合。 1、设计云上架构时&#xff0c;如果能充分了解云服务的特…...

锂电池4.2V升压24V推荐哪些升压芯片?高效率国产SL4013输入耐压2.7V-25V

SL4013作为一款高性能升压型DC-DC转换芯片&#xff0c;在单节锂电池&#xff08;4.2V&#xff09;升压至24V的应用中展现出以下核心优势&#xff1a; 一、宽输入电压适应性 SL4013支持2.7V-25V的输入范围&#xff0c;尤其适合单节锂电池&#xff08;3.7V-4.2V&#xff09;的宽…...

中电金信联合阿里云推出智能陪练Agent

在金融业加速数智化转型的今天&#xff0c;提升服务效率与改善用户体验已成为行业升级的核心方向。面对这一趋势&#xff0c;智能体与智能陪练的结合应用&#xff0c;正帮助金融机构突破传统业务模式&#xff0c;开拓更具竞争力的创新机遇。 在近日召开的阿里云AI势能大会期间&…...

基于扣子(Coze.cn)与火山引擎构建高性能智能体的实践指南

1. 引言 1.1. 背景与目标 人工智能&#xff08;AI&#xff09;智能体&#xff08;Agent&#xff09;平台的兴起&#xff0c;例如扣子&#xff08;Coze&#xff09;&#xff0c;正显著改变我们构建复杂 AI 应用的方式 1。这些平台旨在降低技术门槛&#xff0c;让不同技能水平的…...

w~视觉~3D~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/13766161 #Sin3DGen 最近有点忙 可能给忘了,贴了我只是搬运工 发这些给自己看, 还有下面不是隐藏是发布出去 ~ 北京大学xxx团队联合山东大学和xxx AI Lab的研究人员&#xff0c;提出了首个基于单样例场景无需训练便可生…...

SAS宏核心知识与实战应用

1. SAS宏基础 1.1 核心概念 1.1.1 宏处理器 宏处理器在SAS程序运行前执行,用于生成动态代码,可实现代码的灵活定制。 通过宏处理器,可基于输入参数动态生成不同的SAS代码,提高代码复用性。 1.1.2 宏变量 宏变量是存储文本值的容器,用&符号引用,如&var,用于存储…...

windows使用openssl生成IIS自签证书全流程

使用 OpenSSL 生成适用于 IIS 的证书&#xff0c;通常需要经过以下步骤&#xff1a;生成私钥、生成证书签名请求&#xff08;CSR&#xff09;、生成自签名证书或通过 CA 签名&#xff0c;最后将证书转换为 IIS 所需的 PFX 格式。以下是详细步骤&#xff1a; 1. 安装 OpenSSL …...

笔记本电脑研发笔记:BIOS,Driver,Preloader详记

在笔记本电脑的研发过程中&#xff0c;Driver&#xff08;驱动程序&#xff09;、BIOS&#xff08;基本输入输出系统&#xff09;和 Preloader&#xff08;预加载程序&#xff09;之间存在着密切的相互关系和影响&#xff0c;具体如下&#xff1a; 相互关系 BIOS 与 Preload…...

鸿蒙生态:鸿蒙生态校园行心得

&#xff08;个人观点&#xff0c;仅供参考&#xff09; 兄弟们&#xff0c;今天来浅浅聊一聊这次的设立在长沙的鸿蒙生态行活动。 老样子&#xff0c;我们先来了解一下这个活动&#xff1a; &#xff28;&#xff41;&#xff52;&#xff4d;&#xff4f;&#xff4e;&#x…...

云原生周刊:KubeSphere 平滑升级

开源项目推荐 Kagent Kagent 是一个开源的 K8s 原生框架&#xff0c;旨在帮助 DevOps 和平台工程师在 K8s 环境中构建和运行 AI 代理&#xff08;Agentic AI&#xff09;。与传统的生成式 AI 工具不同&#xff0c;Kagent 强调自主推理和多步骤任务的自动化执行&#xff0c;适…...

Uniapp:swiper(滑块视图容器)

目录 一、基本概述二、属性说明三、基本使用 一、基本概述 一般用于左右滑动或上下滑动&#xff0c;比如banner轮播图 二、属性说明 属性名类型默认值说明平台差异说明indicator-dotsBooleanfalse是否显示面板指示点indicator-colorColorrgba(0, 0, 0, .3)指示点颜色indicat…...

开源的自动驾驶模拟器

以下是目前主流的 ‌开源自动驾驶模拟器‌&#xff0c;适用于算法开发、测试和研究&#xff1a; ‌1. CARLA‌ ‌官网/GitHub‌: carla.org | GitHub‌许可证‌: MIT‌特点‌: 基于虚幻引擎&#xff08;Unreal Engine&#xff09;&#xff0c;提供高保真城市场景&#xff08;支…...

Uniapp:scroll-view(区域滑动视图)

目录 一、基本概述二、属性说明三、基本使用3.1 纵向滚动3.2 横向滚动 一、基本概述 scroll-view&#xff0c;可滚动视图区域。用于区域滚动。 二、属性说明 属性名类型默认值说明平台差异说明scroll-xBooleanfalse允许横向滚动scroll-yBooleanfalse允许纵向滚动 三、基本使…...

【漏洞复现】Struts2系列

【漏洞复现】Struts2系列 1. 了解Struts21. Struts2 S2-061 RCE &#xff08;CVE-2020-17530&#xff09;1. 漏洞描述2. 影响版本3. 复现过程 1. 了解Struts2 Apache Struts2是一个基于MVC设计模式的Web应用框架&#xff0c;会对某些标签属性&#xff08;比如 id&#xff09;的…...

洗车小程序系统前端uniapp 后台thinkphp

洗车小程序系统 前端uniapp 后台thinkphp 支持多门店 分销 在线预约 套餐卡等...

【RuleUtil】适用于全业务场景的规则匹配快速开发工具

一、RuleUtil 开发背景 1.1 越来越多&#xff0c;越来越复杂的业务规则 1、规则的应用场景多 2、规则配置的参数类型多&#xff08;ID、数值、文本、日期等等&#xff09; 3、规则的参数条件多&#xff08;大于、小于、等于、包含、不包含、区间等等&#xff09; 4、规则的结…...

多表查询之嵌套查询

目录 引言 一、标量嵌套查询 二、列嵌套查询 三、行嵌套查询 四、表嵌套查询 引言 1、概念 SQL语句中嵌套 select 语句&#xff0c;称为嵌套查询&#xff0c;又称子查询。嵌套查询外部的语句可以是 insert / update / delete / select 的任何一个。 嵌套…...

js原型链prototype解释

function Person(){} var personnew Person() console.log(啊啊,Person instanceof Function);//true console.log(,Person.__proto__Function.prototype);//true console.log(,Person.prototype.__proto__ Object.prototype);//true console.log(,Function.prototype.__prot…...

RK3588 ubuntu20禁用自带的TF卡挂载,并设置udev自动挂载

禁用系统的自动挂载&#xff08;udisks2&#xff09; sudo vim /etc/udev/rules.d/80-disable-automount.rules添加 ACTION"add", KERNEL"mmcblk1p1", ENV{UDISKS_IGNORE}"1"KERNEL“mmcblk1p1”&#xff1a;匹配设备名&#xff08;TF卡通常是…...

【Pytorch 中的扩散模型】去噪扩散概率模型(DDPM)的实现

介绍 广义上讲&#xff0c;扩散模型是一种生成式深度学习模型&#xff0c;它通过学习到的去噪过程来创建数据。扩散模型有很多变体&#xff0c;其中最流行的通常是文本条件模型&#xff0c;它可以根据提示生成特定的图像。一些扩散模型&#xff08;例如 Control-Net&#xff0…...