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

Sentinel源码—4.FlowSlot实现流控的原理一

大纲

1.FlowSlot根据流控规则对请求进行限流

2.FlowSlot实现流控规则的快速失败效果的原理

3.FlowSlot实现流控规则中排队等待效果的原理

4.FlowSlot实现流控规则中Warm Up效果的原理

1.FlowSlot根据流控规则对请求进行限流

(1)流控规则FlowRule的配置Demo

(2)注册流控监听器和加载流控规则

(3)FlowSlot根据流控规则对请求进行限流

(1)流控规则FlowRule的配置Demo

从下图可知,流控规则包含以下属性:

一.规则id、资源名、针对来源

这三个属性是所有规则共有的属性,会分别封装到AbstractRule的id、resource、limitApp字段中,各个具体的规则子类都会继承AbstractRule类。

二.阈值类型

阈值类型包括QPS和并发线程数两个选项,对应FlowRule中的字段为grade。

三.单机阈值

单机阈值也就是限流阈值。无论是基于QPS还是并发线程数,都要设置限流阈值,对应FlowRule中的字段为count。

四.是否集群

是否集群是一个boolean类型的字段,对应FlowRule中的字段为clusterMode。true表示开启集群模式,false表示单机模式;

五.流控模式

流控模式有三种:直接模式、关联模式和链路模式,对应FlowRule中的字段为strategy。

六.关联资源

当流控模式选择为关联时,此值含义是关联资源,当流控模式选择为链路时,此值含义是入口资源。所以仅当流控模式选择关联和链路时,才对应FlowRule中的字段为refResource。

七.流控效果

流控效果有三种:快速失败、Warm Up、排队等待。还有一个选项未在页面上显示,即Warm Up和排队等待的结合体。也就是Warm Up + 排队等待,对应FlowRule中的字段为controlBehavior。

八.流控控制器

由于流控有多种模式,而每种模式都会对应一个模式流量整形控制器。所以流控规则FlowRule中会有一个字段TrafficShapingController,用来实现不同流控模式下的不同流控效果。

九.预热时长

此选项仅在流控效果选择Warm Up时出现,表示Warm Up的时长,对应FlowRule中的字段为warmUpPeriodSec。

十.超时时间

此选项仅在流控效果选择排队等待时出现,表示超出流控阈值后,排队等待多久才抛出异常,对应FlowRule中的字段为maxQueueingTimeMs。

public abstract class AbstractRule implements Rule {//rule id. 规则idprivate Long id;//Resource name. 资源名称private String resource;//针对来源,默认是default//多个来源使用逗号隔开,比如黑名单规则,限制userId是1和3的访问,那么就设置limitApp为"1,3"//Application name that will be limited by origin.//The default limitApp is {@code default}, which means allowing all origin apps.//For authority rules, multiple origin name can be separated with comma (',').private String limitApp;...
}//规则id、资源名(resource)、针对来源(limitApp),这三个字段在父类AbstractRule里
//Each flow rule is mainly composed of three factors: grade, strategy and controlBehavior:
//The grade represents the threshold type of flow control (by QPS or thread count).
//The strategy represents the strategy based on invocation relation.
//The controlBehavior represents the QPS shaping behavior (actions on incoming request when QPS exceeds the threshold.
public class FlowRule extends AbstractRule {public FlowRule() {super();setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);}public FlowRule(String resourceName) {super();setResource(resourceName);setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);}//The threshold type of flow control (0: thread count, 1: QPS).//阈值类型:1代表QPS;0代表并发线程数private int grade = RuleConstant.FLOW_GRADE_QPS;//Flow control threshold count.//单机阈值:也就是限流数private double count;//Flow control strategy based on invocation chain.//流控模式:0代表直接;1代表关联;2代表链路//RuleConstant#STRATEGY_DIRECT for direct flow control (by origin);//RuleConstant#STRATEGY_RELATE for relevant flow control (with relevant resource);//RuleConstant#STRATEGY_CHAIN for chain flow control (by entrance resource).private int strategy = RuleConstant.STRATEGY_DIRECT;//关联资源,当流控模式选择为关联时,此值含义是关联资源,当流控模式选择为链路时,此值含义是入口资源//Reference resource in flow control with relevant resource or context.private String refResource;//Rate limiter control behavior.//0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter//流控效果:0代表快速失败, 1代表Warm Up, 2代表排队等待, 3代表Warm Up + 排队等待private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;//预热时长:只有当流控效果选择为Warm Up时才会出现private int warmUpPeriodSec = 10;//Max queueing time in rate limiter behavior.//超时时间:只有当流控效果选择排队等待时才会出现private int maxQueueingTimeMs = 500;//是否集群:默认false表示单机private boolean clusterMode;//Flow rule config for cluster mode.//集群配置private ClusterFlowConfig clusterConfig;//The traffic shaping (throttling) controller.//流量整形控制器:实现[流控效果]的四种不同模式private TrafficShapingController controller;...
}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.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}}}}
}

(2)注册流控监听器和加载流控规则

一.Sentinel监听器模式的处理流程

Sentinel监听器模式会包含三大角色:

角色一:监听器PropertyListener

角色二:监听器管理器SentinelProperty

角色三:规则管理器RuleManager

首先,规则管理器RuleManager在初始化时,会调用监听器管理器SentinelProperty的addListener()方法将监听器PropertyListener注册到监听器管理器SentinelProperty中。

然后,使用方使用具体的规则时,可以通过调用规则管理器RuleManager的loadRules()方法加载规则。加载规则时会调用监听器管理器SentinelProperty的updateValue()方法通知每一个监听器PropertyListener,也就是通过监听器PropertyListener的configUpdate()方法把规则加载到规则管理器RuleManager的本地中。

二.注册流控监听器和加载流控规则

需要注意的是:加载流控规则时会调用FlowRuleUtil的generateRater()方法,根据不同的流控效果选择不同的流量整形控制器TrafficShapingController。

//One resources can have multiple rules. 
//And these rules take effects in the following order:
//requests from specified caller
//no specified caller
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>>();...static {//将流控规则监听器注册到监听器管理器中currentProperty.addListener(LISTENER);startMetricTimerListener();}//Load FlowRules, former rules will be replaced.//加载流控规则public static void loadRules(List<FlowRule> rules) {//通知监听器管理器中的每一个监听器,规则已发生变化,需要重新加载规则配置//其实就是更新FlowRuleManager规则管理器中的流控规则列表flowRulescurrentProperty.updateValue(rules);}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 {private static final Function<FlowRule, String> extractResource = new Function<FlowRule, String>() {@Overridepublic String apply(FlowRule rule) {return rule.getResource();}};...//Build the flow rule map from raw list of flow rules, grouping by resource name.public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list) {return buildFlowRuleMap(list, null);}//Build the flow rule map from raw list of flow rules, grouping by resource name.public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter) {return buildFlowRuleMap(list, filter, true);}//Build the flow rule map from raw list of flow rules, grouping by resource name.public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter, boolean shouldSort) {return buildFlowRuleMap(list, extractResource, filter, shouldSort);}//Build the flow rule map from raw list of flow rules, grouping by provided group function.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) {...//获取[流控效果]流量整形控制器TrafficShapingControllerTrafficShapingController 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(/*@Valid*/ 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());}...
}

(3)FlowSlot根据流控规则对请求进行限流

FlowSlot中处理限流的核心方法其实是FlowRuleChecker.checkFlow()。该方法首先会从FlowRuleManager.flowRules中获取资源对应的流控规则列表,然后再遍历规则列表并调用canPassCheck()方法验证当前请求是否命中规则。如果命中规则,则抛出异常。如果没命中规则,则允许请求通过。

FlowRuleChecker的canPassCheck()方法会判断规则是否是集群模式。如果流控规则是集群模式,则调用passClusterCheck()方法。如果流控规则不是集群模式,则调用passLocalCheck()方法。

在FlowRuleChecker的passLocalCheck()方法中,首先会根据流控规则选择合适的Node作为限流计算的依据,然后再通过TrafficShapingController的canPass()方法判断是否放行请求。

其中选择合适的Node作为限流计算的依据时,会调用selectNodeByRequesterAndStrategy()方法根据流控规则的针对来源、流控模式和当前请求的来源,从下面三个节点中选择一个作为合适的Node:

节点一:上下文中的来源节点OriginNode

节点二:当前请求的集群节点ClusterNode

节点三:默认节点DefaultNode

注意:limitApp是流控规则里的字段,代表此规则生效时的针对来源。而origin是Context里的字段,代表当前请求的来源是什么。假设limitApp配置的是shop,某个请求的origin也是shop,则规则生效。如果limitApp采取默认值default,则代表全部origin都生效。

@Spi(order = Constants.ORDER_FLOW_SLOT)
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {private final FlowRuleChecker checker;private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {@Overridepublic Collection<FlowRule> apply(String resource) {//Flow rule map should not be null.Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();return flowRules.get(resource);}};public FlowSlot() {this(new FlowRuleChecker());}FlowSlot(FlowRuleChecker checker) {AssertUtil.notNull(checker, "flow checker should not be null");this.checker = checker;}@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {//检查流控规则,count默认是1checkFlow(resourceWrapper, context, node, count, prioritized);fireEntry(context, resourceWrapper, node, count, prioritized, args);}void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {//调用规则检查器FlowRuleChecker的checkFlow()方法进行检查,count默认是1checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {fireExit(context, resourceWrapper, count, args);}
}//Rule checker for flow control rules.
public class FlowRuleChecker {public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {if (ruleProvider == null || resource == null) {return;}//从Map中获取resource资源对应的流控规则列表Collection<FlowRule> rules = ruleProvider.apply(resource.getName());if (rules != null) {//循环遍历每一个流控规则for (FlowRule rule : rules) {//调用canPassCheck方法进行流控规则验证,判断此次请求是否命中针对resource资源配置的流控规则//传入的参数count默认是1if (!canPassCheck(rule, context, node, count, prioritized)) {throw new FlowException(rule.getLimitApp(), rule);}}}}public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount) {return canPassCheck(rule, context, node, acquireCount, false);}public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {//参数校验String limitApp = rule.getLimitApp();if (limitApp == null) {return true;}//如果是集群模式,则执行passClusterCheck()方法if (rule.isClusterMode()) {return passClusterCheck(rule, context, node, acquireCount, prioritized);}//如果是单机模式,则执行passLocalCheck()方法,acquireCount默认是1return passLocalCheck(rule, context, node, acquireCount, prioritized);}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()方法对请求进行检查,acquireCount默认是1return rule.getRater().canPass(selectedNode, acquireCount, prioritized);}//选择Node作为限流计算的依据static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {//The limit app should not be empty.//获取流控规则中配置的"针对来源",默认是defaultString limitApp = rule.getLimitApp();//获取流控规则中配置的"流控模式",0代表直接,1代表关联,2代表链路int strategy = rule.getStrategy();//从context对象中获取当前请求的来源String origin = context.getOrigin();//情形一:当流控规则的针对来源(limitApp)与当前请求的来源(origin)相同时//这种情况表示该限流规则针对特定来源进行限流//如果配置了针对app1进行限流,那么app2就不会生效,这就是针对特定来源进行限流if (limitApp.equals(origin) && filterOrigin(origin)) {//如果流控规则中配置的"流控模式"是直接(RuleConstant.STRATEGY_DIRECT),则返回上下文中的Origin Node//因为这种模式要求根据调用方的情况进行限流,而Origin Node包含了调用方的统计信息,所以选择Origin Node作为限流计算的依据if (strategy == RuleConstant.STRATEGY_DIRECT) {//Matches limit origin, return origin statistic node.return context.getOriginNode();}//如果流控规则中配置的"流控模式"是关联、链路(RuleConstant.STRATEGY_RELATE或RuleConstant.STRATEGY_CHAIN),则调用selectReferenceNode()方法//此方法会判断://如果"流控模式"是关联(RuleConstant.STRATEGY_RELATE),则返回关联资源的ClusterNode//如果"流控模式"是链路(RuleConstant.STRATEGY_CHAIN),则返回DefaultNodereturn selectReferenceNode(rule, context, node);}//情况二:当流控规则的针对来源(limitApp)是默认值(RuleConstant.LIMIT_APP_DEFAULT)时//这种情况表示该流控规则对所有来源都生效else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {//如果流控规则中配置的"流控模式"是直接(RuleConstant.STRATEGY_DIRECT),则返回当前请求的集群节点ClusterNode//因为此时要求的是根据被调用资源的情况进行限流,而集群节点包含了被调用资源的统计信息,所以选择集群节点作为限流计算的依据if (strategy == RuleConstant.STRATEGY_DIRECT) {//Return the cluster node.return node.getClusterNode();}//如果流控规则中配置的"流控模式"是关联、链路(RuleConstant.STRATEGY_RELATE或RuleConstant.STRATEGY_CHAIN),则调用selectReferenceNode()方法//此方法会判断://如果"流控模式"是关联(RuleConstant.STRATEGY_RELATE),则返回关联资源的ClusterNode//如果"流控模式"是链路(RuleConstant.STRATEGY_CHAIN),则返回DefaultNodereturn selectReferenceNode(rule, context, node);}//情况三:当流控规则的针对来源(limitApp)是其他(RuleConstant.LIMIT_APP_OTHER),且当前请求的来源(origin)与流控规则的资源名(rule.getResource())不同时//这种情况表示该流控规则针对除默认来源以外的其他来源进行限流,可实现个性化限流//比如可以对app1进行个性化限流,对其他所有app进行整体限流else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp) && FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {//如果流控规则中配置的"流控模式"是直接(RuleConstant.STRATEGY_DIRECT),则返回上下文中的来源节点(Origin Node)//因为这种"流控模式"要求根据调用方的情况进行限流,而来源节点包含了调用方的统计信息,所以选择来源节点作为限流计算的依据if (strategy == RuleConstant.STRATEGY_DIRECT) {return context.getOriginNode();}//如果流控规则中配置的"流控模式"是关联、链路(RuleConstant.STRATEGY_RELATE或RuleConstant.STRATEGY_CHAIN),则调用selectReferenceNode()方法//此方法会判断://如果"流控模式"是关联(RuleConstant.STRATEGY_RELATE),则返回关联资源的ClusterNode//如果"流控模式"是链路(RuleConstant.STRATEGY_CHAIN),则返回DefaultNodereturn selectReferenceNode(rule, context, node);}return null;}//如果流控规则中配置的"流控模式"是关联(RuleConstant.STRATEGY_RELATE),则返回关联资源的ClusterNode//如果流控规则中配置的"流控模式"是链路(RuleConstant.STRATEGY_CHAIN),则返回DefaultNodestatic Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {String refResource = rule.getRefResource();int strategy = rule.getStrategy();if (StringUtil.isEmpty(refResource)) {return null;}if (strategy == RuleConstant.STRATEGY_RELATE) {//关联资源的ClusterNodereturn ClusterBuilderSlot.getClusterNode(refResource);}if (strategy == RuleConstant.STRATEGY_CHAIN) {if (!refResource.equals(context.getName())) {return null;}return node;}//No node.return null;}private static boolean filterOrigin(String origin) {// Origin cannot be `default` or `other`.return !RuleConstant.LIMIT_APP_DEFAULT.equals(origin) && !RuleConstant.LIMIT_APP_OTHER.equals(origin);}...
}

2.FlowSlot实现流控规则的快速失败效果的原理

(1)流控效果为快速失败时对应的流量整形控制器

(2)流量整形控制器DefaultController执行分析

(3)流控模式中的关联模式和链路模式说明

(1)流控效果为快速失败时对应的流量整形控制器

流控效果有:快速失败、Warm Up、排队等待。

调用FlowRuleManager的loadRules()方法加载流控规则时,会触发执行FlowPropertyListener的configUpdate()方法,该方法又会调用FlowRuleUtil的generateRater()方法。在FlowRuleUtil的generateRater()方法中,便会根据不同的流控效果选择不同的流量整形控制器。

其中当流控规则的流控效果为快速失败时,对应的流量整形控制器为TrafficShapingController的子类DefaultController。

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>>();...static {//将流控规则监听器注册到监听器管理器中currentProperty.addListener(LISTENER);startMetricTimerListener();}//Load FlowRules, former rules will be replaced.//加载流控规则public static void loadRules(List<FlowRule> rules) {//通知监听器管理器中的每一个监听器,规则已发生变化,需要重新加载规则配置//其实就是更新FlowRuleManager规则管理器中的流控规则列表flowRulescurrentProperty.updateValue(rules);}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 {private static final Function<FlowRule, String> extractResource = new Function<FlowRule, String>() {@Overridepublic String apply(FlowRule rule) {return rule.getResource();}};...//Build the flow rule map from raw list of flow rules, grouping by provided group function.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) {...//获取[流控效果]流量整形控制器TrafficShapingControllerTrafficShapingController 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(/*@Valid*/ 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());}...
}

当FlowSlot的entry()方法对请求进行流控规则验证时,会调用规则检查器FlowRuleChecker的checkFlow()方法进行检查,最终会通过FlowRule的getRater()方法获取流控规则对应的流量整形控制器,然后调用TrafficShapingController的canPass()方法对请求进行检查。当流控效果为快速失败时,具体调用的其实就是DefaultController的canPass()方法。

@Spi(order = Constants.ORDER_FLOW_SLOT)
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {private final FlowRuleChecker checker;private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {@Overridepublic Collection<FlowRule> apply(String resource) {//Flow rule map should not be null.Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();return flowRules.get(resource);}};...@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {//检查流控规则  checkFlow(resourceWrapper, context, node, count, prioritized);fireEntry(context, resourceWrapper, node, count, prioritized, args);}void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {//调用规则检查器FlowRuleChecker的checkFlow()方法进行检查checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {fireExit(context, resourceWrapper, count, args);}
}//Rule checker for flow control rules.
public class FlowRuleChecker {public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {if (ruleProvider == null || resource == null) {return;}//从Map中获取resource资源对应的流控规则列表Collection<FlowRule> rules = ruleProvider.apply(resource.getName());if (rules != null) {//循环遍历每一个流控规则for (FlowRule rule : rules) {//调用canPassCheck方法进行流控规则验证,判断此次请求是否命中针对resource资源配置的流控规则if (!canPassCheck(rule, context, node, count, prioritized)) {throw new FlowException(rule.getLimitApp(), rule);}}}}public boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount) {return canPassCheck(rule, context, node, acquireCount, false);}public boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {//参数校验String limitApp = rule.getLimitApp();if (limitApp == null) {return true;}//如果是集群模式,则执行passClusterCheck()方法if (rule.isClusterMode()) {return passClusterCheck(rule, context, node, acquireCount, prioritized);}//如果是单机模式,则执行passLocalCheck()方法return passLocalCheck(rule, context, node, acquireCount, prioritized);}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);}...
}//Default throttling controller (immediately reject strategy).
public class DefaultController implements TrafficShapingController {private static final int DEFAULT_AVG_USED_TOKENS = 0;private double count;private int grade;public DefaultController(double count, int grade) {this.count = count;this.grade = grade;}@Overridepublic boolean canPass(Node node, int acquireCount) {return canPass(node, acquireCount, false);}@Overridepublic boolean canPass(Node node, int acquireCount, boolean prioritized) {//获取当前请求数int curCount = avgUsedTokens(node);//如果当前请求数 + 1超出阈值,那么返回失败if (curCount + acquireCount > count) {//进行优先级逻辑处理if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {long currentTime;long waitInMs;currentTime = TimeUtil.currentTimeMillis();waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {node.addWaitingRequest(currentTime + waitInMs, acquireCount);node.addOccupiedPass(acquireCount);sleep(waitInMs);//PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}.throw new PriorityWaitException(waitInMs);}}return false;}//如果当前请求数+1没有超出阈值,则返回成功return true;}private int avgUsedTokens(Node node) {if (node == null) {return DEFAULT_AVG_USED_TOKENS;}return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());}private void sleep(long timeMillis) {try {Thread.sleep(timeMillis);} catch (InterruptedException e) {//Ignore.}}
}

(2)流量整形控制器DefaultController执行分析

流控效果为快速失败时,会调用DefaultController的canPass()方法。

问题一:其中的入参Node是如何选择的

Node的选择主要依赖于三个参数:FlowRule的limitApp、FlowRule的strategy、Context的origin,这三个参数分别表示流控规则的针对来源、流控模式和当前请求的来源。

情形一:如果limitApp和origin相等并且limitApp不是默认值default

此时的流控规则便是针对特定调用方的
如果流控模式为直接,那么选择的Node是OriginNode
如果流控模式为关联,那么选择的Node是关联资源的ClusterNode
如果流控模式为链路,那么选择的Node是DefaultNode

情形二:limitApp值为默认的default

如果流控模式为直接,那么选择的Node是当前资源的ClusterNode
如果流控模式为关联,那么选择的Node是关联资源的ClusterNode
如果流控模式为链路,那么选择的Node是DefaultNode

情形三:limitApp值为other且origin与FlowRule.getResource()不同

如果流控模式为直接,那么选择的Node是OriginNode
如果流控模式为关联,那么选择的Node是关联资源的ClusterNode
如果流控模式为链路,那么选择的Node是DefaultNode

问题二:如何判断阈值类型是QPS还是线程

FlowRule规则类里有个名为grade的字段,代表着阈值类型。所以在初始化DefaultController时就会传入流控规则FlowRule的grade值,这样在DefaultController的avgUsedTokens()方法中,就可以根据grade字段的值判断出阈值类型是QPS还是线程数了。

问题三:其中的入参prioritized的作用是什么

DefaultController的canPass()的入参prioritized表示是否对请求设置优先级。当prioritized为true时,表示该请求具有较高优先级的请求。当prioritized为false时,表示该请求是普通请求。而高优先级的请求需要尽量保证其可以通过限流检查。不过一般情况下,prioritized的值默认为false,除非手动指定为true,毕竟限流的目的是不想让超出的流量通过。

(3)流控模式中的关联模式和链路模式说明

一.关联模式

关联流控模式中,可以将两个资源进行关联。当某个资源的流量超限时,可以触发其他资源的流控规则。

比如用户下单购物时会涉及下单资源和支付资源,如果支付资源达到流控阈值,那么应该要同时禁止下单,也就是通过支付资源来关联到下单资源。

注意:如果采取关联模式,那么设置的QPS阈值是被关联者的,而非关联者的。在如下图示中配置了QPS的阈值为3,这是针对testPay资源设置的,而不是针对testOrder资源设置的。也就是testOrder被流控的时机就是当testPay的QPS达到3的时候,3并不是testOrder所访问的次数,而是testPay这个接口被访问的次数。

二.链路模式

一个资源可能会被多个业务链路调用,不同的业务链路需要进行不同的流控,这时就可以使用链路模式。

下图便为testTrace资源创建了一条链路模式的流控规则,规则为QPS限制到1 ,且链路入口资源为/trace/test2。

这意味着:/trace/test1链路可以随便访问testTrace资源,不受任何限制。/trace/test2链路访问testTrace资源时会限制QPS为1,超出限制被流控。

相关文章:

Sentinel源码—4.FlowSlot实现流控的原理一

大纲 1.FlowSlot根据流控规则对请求进行限流 2.FlowSlot实现流控规则的快速失败效果的原理 3.FlowSlot实现流控规则中排队等待效果的原理 4.FlowSlot实现流控规则中Warm Up效果的原理 1.FlowSlot根据流控规则对请求进行限流 (1)流控规则FlowRule的配置Demo (2)注册流控监…...

跟康师傅学Java-基础语法

跟康师傅学Java-基础SE 一、Java语言概述 1. 基本概念 什么是Java?干什么的? 前端是服务员,java做后台的,服务器,好比餐馆的厨师! Java之父:詹姆斯.高斯林(James Gosling) 软件:一系列按照特定顺序组织的计算机数据和指令的集合。分为系统软件和应用软件。 程序…...

Java语言实现递归调用算法

1. 递归调用原理 递归是一种编程技巧&#xff0c;其中函数直接或间接地调用自身。递归的核心思想是将一个复杂问题分解为更小的子问题&#xff0c;直到问题变得足够简单可以直接解决。递归通常包含两个部分&#xff1a; 1. 基础情况&#xff08;Base Case&#xff09;&#xff…...

【数据结构_10】二叉树(1)

一、树 树是一种非线性的数据结构&#xff0c;是由n个有限节点组成一个具有层次关系的集合。树的每个节点能够延伸出多个子节点&#xff0c;但每个子节点只能由一个父节点。 树形结构中&#xff0c;子树之间不能有交集&#xff0c;否则就不是树形结构。 二、树的表示形式 1…...

c++:智能指针

1.智能指针使用场景与优势 void Func() { int* array1 new int[10]; int* array2 new int[10]; try { int len, time; cin >> len >> time; cout << Divide(len, time) << endl; } catch (...) { cout << "delete []" << arr…...

RISC-V简介

RISC-V简介 1. RISC-V RISC-V&#xff08;发音为“riskfive”&#xff09;是一个基于精简指令集&#xff08;RISC&#xff09;原则的全新开源指令集架构&#xff08;ISA&#xff09;。其中的字母“V”包含两层意思&#xff0c;一是这是Berkeley从RISCI开始设计的第五代指令集…...

Google Test 与 Google Mock:C++ 测试与模拟的完美结合

Google Test 与 Google Mock&#xff1a;C 测试与模拟的完美结合 摘要 本文深入解析 Google Test&#xff08;GTest&#xff09;和 Google Mock&#xff08;GMock&#xff09;的核心功能与使用方法&#xff0c;探讨两者在 C 项目中的联合应用及集成策略。通过详细的功能介绍、…...

c语言数据结构----------二叉排序树

#include <stdio.h> #include <malloc.h>//定义二叉排序树 typedef struct BSTnode {int key; //节点值int keyNull; //便于地址传递struct BSTnode *lchild;struct BSTnode *rchild; } BSTnode;//往二叉排序树插入结点 int BSTInsert(BSTnode *T, int k) {if (…...

Sysstat学习

Sysstat&#xff08;System Statistics&#xff09;是一个功能强大的开源工具集&#xff0c;用于监控 Linux 系统的性能和资源使用情况&#xff0c;特别适用于 Ubuntu 系统。它包含多个工具&#xff0c;如 sar、iostat、mpstat 和 pidstat&#xff0c;帮助系统管理员实时或历史…...

智能体开发范式革命:Cangjie Magic的颠覆性创新与行业重塑

开篇:一场静悄悄的技术革命 2025年春季,人工智能领域发生了一场意义深远却鲜为人知的变革。仓颉社区推出的Cangjie Magic智能体开发平台,正以润物细无声的方式重塑着AI应用的构建范式。这并非简单的工具迭代,而是一次从底层逻辑到顶层设计的全面革新。本文将带领读者深入探…...

k8s 下 java 服务出现 OOM 后获取 dump 文件

文章目录 背景解决第 1 步:通过 Dockerfile 挂载 NFS 盘第 2 步:修改 dump 路径为 NFS 盘路径第 3 步:OOM dump 验证参考背景 😂 背景:项目部署在RainBond(k8s)环境下,容器出现 OOM 异常后,k8s 会自动进行滚动更新。 恰恰因为滚动更新,会导致原来的容器被删除。这…...

16位海明码解码电路设计教程

## 1. 海明码基本原理 ### 1.1 什么是海明码 海明码(Hamming Code)是一种能够检测并纠正单比特错误的纠错码&#xff0c;由理查德海明(Richard Hamming)于1950年发明。它通过添加几个校验位(奇偶校验位)到原始数据中&#xff0c;使得数据在传输过程中发生单比特错误时能够被检测…...

九、数据库day01--认识

文章目录 一、认识数据库1.数据库分类关系型数据库核⼼要素示例 2. SQL 语⾔3. MySQL 数据库介绍4. 数据库连接⼯具 Navicat连接数据库操作步骤 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、认识数据库 说明: 数据库是专⻔⽤来存储数据的软…...

2.深入剖析 Rust+Axum 类型安全路由系统

摘要 详细解读 RustAxum 路由系统的关键设计原理&#xff0c;涵盖基于 Rust 类型系统的路由匹配机制、动态路径参数与正则表达式验证以及嵌套路由与模块化组织等多种特性。 一、引言 在现代 Web 开发中&#xff0c;路由系统是构建 Web 应用的核心组件之一&#xff0c;它负责…...

深度学习 从入门到精通 day_02

1. 自动微分 自动微分模块torch.autograd负责自动计算张量操作的梯度&#xff0c;具有自动求导功能。自动微分模块是构成神经网络训练的必要模块&#xff0c;可以实现网络权重参数的更新&#xff0c;使得反向传播算法的实现变得简单而高效。 1.1 基础概念 1. 张量 &#xff1a…...

Selenium 实现自动化分页处理与信息提取

Selenium 实现自动化分页处理与信息提取 在 Web 自动化测试或数据抓取场景中&#xff0c;分页处理是一个常见的需求。通过 Selenium&#xff0c;我们可以实现对多页面内容的自动遍历&#xff0c;并从中提取所需的信息。本文将详细介绍如何利用 Selenium 进行自动化分页处理和信…...

【系统搭建】DPDK实现两虚拟机基于testpmd和l2fwd的收发包

testpmd与l2fwd的配合构建一个高性能的虚拟网络测试环境。l2fwd服务工作在数据链路层&#xff0c;使用MAC地址寻址&#xff0c;很多基于DPDK的策略实现可以基于l2fwd进行开发。 一、拓扑结构示意 ------------------- 虚拟化层网络 ------------------- | 虚拟机1 …...

简单接口工具(ApiCraft-Web)

ApiCraft-Web 项目介绍 ApiCraft-Web 是一个轻量级的 API 测试工具&#xff0c;提供了简洁直观的界面&#xff0c;帮助开发者快速测试和调试 HTTP 接口。 功能特点 支持多种 HTTP 请求方法&#xff08;GET、POST、PUT、DELETE&#xff09;可配置请求参数&#xff08;Query …...

C语言数据类型取值范围

32位C语言整型数据类型取值范围 64位C语言整型数据类型取值范围 C语言标准数据类型保证的取值范围 在编写程序时如果要方便移植&#xff0c;我们应该关注的是图2-11的取值范围。 摘录自《CSAPP》。...

【机器学习】大数据时代,模型训练慢如牛?解锁Spark MLlib与分布式策略

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

合成数据赋能AI:从生成到闭环的全景图谱

目录 合成数据赋能AI&#xff1a;从生成到闭环的全景图谱 &#x1f3af; 项目目标 &#x1f4c4; 白皮书 / PPT 大纲结构 一、合成数据概述&#xff08;What & Why&#xff09; 二、合成数据的核心生成技术&#xff08;How&#xff09; 三、合成数据适配任务&#xf…...

CS144 Lab0实战记录:搭建网络编程基础

文章目录 1 实验概述与背景2 ByteStream的设计与实现2.1 字节流抽象概述2.2 实现思路2.3 核心数据结构2.4 Writer实现细节2.5 Reader实现细节 3 WebGet应用实现 1 实验概述与背景 Stanford大学的CS144课程是计算机网络领域最著名的课程之一&#xff0c;其实验设计巧妙地引导学…...

杂记-LeetCode中部分题思路详解与笔记-HOT100篇-其三

时光荏苒啊&#xff0c;没想到这么快就到了四月份... 这个坑好久没写了&#xff0c;现在我们重启一下。 我看了一下之前的笔记&#xff0c;似乎是停留在了链表部分且HOT100中可以说最重要的链表题之一&#xff1a;LRU缓存居然没有写&#xff0c;真是岂有此理&#xff0c;让我…...

【python画图】:从入门到精通绘制完美柱状图

目录 Python数据可视化&#xff1a;从入门到精通绘制完美柱状图一、基础篇&#xff1a;快速绘制柱状图1.1 使用Matplotlib基础绘制1.2 使用Pandas快速绘图 二、进阶篇&#xff1a;专业级柱状图定制2.1 多系列柱状图2.2 堆叠柱状图2.3 水平柱状图 三、专业参数速查表Matplotlib …...

医疗设备预测性维护的合规性挑战与标准化路径研究

摘要 本研究从医疗设备全生命周期管理视角&#xff0c;探讨预测性维护技术面临的特殊合规性挑战及其标准化解决方案。通过分析全球12个主要医疗市场的监管差异&#xff0c;提出基于ISO 23510的通用合规框架&#xff0c;并验证其在三类典型医疗设备&#xff08;生命支持类、影像…...

使用 XWPFDocument 生成表格时固定列宽度

一、XWPFDocument XWPFTable个性化属性 1.初始默认写法 XWPFTable table document.createTable(n, m); //在文档中创建一个n行m列的表格 table.setWidth("100%"); // 表格占页面100%宽度// 通过getRow获取行进行自定义设置 XWPFTableRow row table.getRow(0); XW…...

抽象的https原理简介

前言 小明和小美是一对好朋友&#xff0c;他们分隔两地&#xff0c;平时经常写信沟通&#xff0c;但是偶然被小明发现他回给小美的信好像被人拆开看过&#xff0c;甚至偷偷被篡改过。 对称加密算法 开头的通信过程比较像HTTP服务器与客户端的通信过程&#xff0c;全明文传输…...

Chakra UI框架中响应式断点

默认的断点&#xff1a;base是默认样式&#xff0c;不带任何媒体查询&#xff0c;适用于所有屏幕。 sm是30em&#xff08;约480px&#xff09; md是48em&#xff08;768px&#xff09; lg是62em&#xff08;992px&#xff09; xl是80em&#xff08;1280px&#xff09; 2xl是96e…...

【cocos creator 3.x】cocos creator2.x项目升级3.x项目改动点

1、基本改动 基本改动&#xff1a;去掉了cc.&#xff0c;改成在顶部添加导入 项目升级时候直接将cc.去掉&#xff0c;根据提示添加引用 node只保留position,scale,rotation,layer 其余属性如opacity&#xff0c;如果需要使用需要在节点手动添加UIOpacity组件 3d层和ui层分开…...

【android telecom 框架分析 01】【基本介绍 2】【BluetoothPhoneService为何没有源码实现】

1. 背景 我们会在很多资料上看到 BluetoothPhoneService 类&#xff0c;但是我们在实际 aosp 中确找不到具体的实现&#xff0c; 这是为何&#xff1f; 这是一个很好的问题&#xff01;虽然在车载蓝牙电话场景中我们经常提到类似 BluetoothPhoneService 的概念&#xff0c;但…...

Linux:进程:进程调度

进程在CPU上运行具有以下特性&#xff1a; 竞争、独⽴、并⾏、并发 竞争性:系统进程数⽬众多&#xff0c;⽽CPU资源很少甚至只有一个&#xff0c;所以进程之间是具有竞争属性的。为 了⾼效完成任务&#xff0c;更合理竞争相关资源&#xff0c;便具有了优先级 独⽴性: 为了避…...

2025年探秘特种设备安全管理 A 证:守护安全的关键凭证​

在现代工业与生活中&#xff0c;特种设备如锅炉、压力容器、电梯、起重机械等广泛应用&#xff0c;它们给我们带来便利的同时&#xff0c;也伴随着较高的安全风险。为了确保这些设备的安全运行&#xff0c;保障人民生命财产安全&#xff0c;特种设备安全管理显得尤为重要&#…...

WebSocket 实现数据实时推送原理

WebSocket 实现数据实时推送的核心机制在于其全双工通信能力和持久的连接特性。以下是其工作原理的详细步骤&#xff1a; 1. 握手阶段&#xff08;HTTP 升级协议&#xff09; 客户端发起请求&#xff1a;通过发送一个带有特殊头部的 HTTP 请求&#xff0c;请求协议升级。 GET …...

快速迭代收缩-阈值算法(FISTA)

文章目录 1. 数学与优化基础2. FISTA 算法的原理、推导与机制3. Matlab 实现4. FISTA 在图像处理与压缩感知中的应用4.1. 基于小波稀疏先验的图像去噪4.2 压缩感知图像重建 1. 数学与优化基础 在许多信号处理与机器学习问题中&#xff0c;我们希望获得稀疏解&#xff0c;即解向…...

XC6SLX100T-2FGG484I 赛灵思 XilinxFPGA Spartan-6

XC6SLX100T-2FGG484I 是Xilinx 推出的Spartan-6 LXT 系列FPGA芯片&#xff0c;采用45nm工艺设计&#xff0c;以高性价比和低功耗为核心 系列定位&#xff1a;Spartan‑6 LXT&#xff0c;中端逻辑与 DSP 加速 逻辑资源&#xff1a;101 261 个逻辑单元&#xff08;LE&#xff0…...

DP 32bit位宽数据扰码实现和仿真

关于DisplayPort 1.4协议中扰码用的16-bit LFSR的移位8个时钟周期后的输出表达式我们已经用迭代的方法推导过&#xff0c;那么移位32个时钟周期的输出表达式同样可以迭代32次推导出&#xff0c;或者将移位8个时钟的输出表达式迭代3次也可以得到。以下就是移位32个时钟周期的输出…...

Electricity Market Optimization 探索系列(V)

本文参考链接link \hspace{1.6em} 众所周知, 社会福利是指消费者剩余和生产者剩余的和&#xff0c;也等价于产品的市值减去产品的成本&#xff0c;在电力市场中也非常关注社会福利这一概念&#xff0c;基于电力商品的同质性的特点&#xff0c;我们引入反价格需求函数来形象地刻…...

vue3 element-plus el-time-picker控制只显示时 分,并且控制可选的开始结束时间

只显示时分 控制只显示时分 HH:mm 控制只显示时分秒 HH:mm:ss 全部代码&#xff1a; <template><el-time-pickerstyle"width: 220px !important;"v-model"timeValue"format"HH:mm"value-format"HH:mm"/> </template&…...

从技术本质到未来演进:全方位解读Web的过去、现在与未来

一、Web的本质定义 Web(万维网)是一种基于**超文本传输协议(HTTP)和统一资源标识符(URI)**构建的分布式信息系统。它的核心在于通过超链接将全球范围内的信息资源连接成网状结构,使任何接入互联网的设备都能访问这些资源。Web的本质特征体现在三个方面: 跨平台性:无论…...

C++十进制与十六进制

在C中&#xff0c;可以使用不同的方式来表示十进制和十六进制数值。下面是一个简单的示例代码&#xff0c;展示了如何在C中表示和输出十进制和十六进制数值&#xff1a; #include <iostream> #include <iomanip>int main() {int decimalValue 255; // 十进制数值…...

MySQL基本语法

本地登录&#xff1a;mysql -u 用户名 -p 查看数据库&#xff1a;show databeases 创建库&#xff1a;create database 名字&#xff1b; 删除库&#xff1a;drop database 名字&#xff1b; 选择库&#xff1a;use 名字&#xff1b; 创建表&#xff1a;create table 表名 在…...

机器学习有多少种算法?当下入门需要全部学习吗?

机器学习算法如同工具箱中的器械——种类繁多却各有专攻。面对数百种公开算法&#xff0c;新手常陷入"学不完"的焦虑。本文将拆解算法体系&#xff0c;为初学者指明高效学习路径。 一、算法森林的全景地图 机器学习算法可按四大维度分类&#xff1a; 监督学习&#…...

【c语言】深入理解指针2

文章目录 一、指针数组指针数组模拟二维数组 二、数组指针二维数组传参的本质 三、字符指针变量四、函数指针变量4.1. 函数指针的应用4.2 两端有趣的代码4.3. typedef关键字4.3.1 typedef 的使用4.3.2. typedef与#define对比 五、函数指针数组函数指针数组的应用 一、指针数组 …...

Nacos

Nacos是阿里巴巴的产品&#xff0c; 现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c;在国内受欢迎程度较高。 官网地址&#xff1a;Redirecting to: https://nacos.io/ GitHub&#xff1a; https://github.com/alibaba/nacos 1.Nacos快入门 Nacos可以直…...

Linux,redis群集模式,主从复制,读写分离

redis的群集模式 主从模式 &#xff08;单项复制&#xff0c;主复制到从&#xff09; 一主两从 一台主机上的一主两从 需要修改三个配置文件 主要端口不一样 redis-8001.conf redis-8002.conf redis-8003.conf 哨兵模式 分布式集群模式 redis 安装部署 1&#xff0c;下载…...

《手环表带保养全攻略:材质、清洁与化学品避坑指南》

系列文章目录 文章目录 系列文章目录前言一、表带材质特性与专属养护方案二、清洁剂使用红黑榜三、家庭清洁实验&#xff1a;化学反应警示录四、保养实践方法论总结 前言 手环作为现代生活的智能伴侣&#xff0c;表带材质选择丰富多样。从柔软亲肤的皮质到耐用耐磨的金属&…...

【Leetcode 每日一题 - 补卡】1534. 统计好三元组

问题背景 给你一个整数数组 a r r arr arr&#xff0c;以及 a 、 b 、 c a、b 、c a、b、c 三个整数。请你统计其中好三元组的数量。 如果三元组 ( a r r [ i ] , a r r [ j ] , a r r [ k ] ) (arr[i], arr[j], arr[k]) (arr[i],arr[j],arr[k]) 满足下列全部条件&#xff…...

医疗设备预测性维护合规架构:从法规遵循到技术实现的深度解析

在医疗行业数字化转型加速推进的当下&#xff0c;医疗设备预测性维护已成为提升设备可用性、保障医疗安全的核心技术。然而&#xff0c;该技术的有效落地必须建立在严格的合规框架之上。医疗设备直接关乎患者生命健康&#xff0c;其维护过程涉及医疗法规、数据安全、质量管控等…...

如何在 IntelliJ IDEA 中安装 FindBugs-IDEA 1.0.1

以下是 FindBugs-IDEA 1.0.1 插件在 IntelliJ IDEA 中的安装步骤&#xff08;适用于较旧版本的 IDEA&#xff0c;新版本可能需使用替代插件如 SpotBugs&#xff09;&#xff1a; 方法一&#xff1a;手动下载安装&#xff08;适用于无法通过市场安装的情况&#xff09; 下载插件…...

小车正常但是加载不出地图 找不到mapserver

Request for map failed; trying again... 找不到mapserver 原因&#xff1a; bash [ERROR] [1744895448.714854952]: failed to open image file "/home/liyb/catkin_ws/src/nav_demo/map/crossing.pgm": Couldnt open /home/xxx/catkin_ws/src/nav_demo/map/cr…...