Sentinel源码—3.ProcessorSlot的执行过程二

从门禁到支付:Uniapp+NFC开发避坑指南(支持Mifare Classic) 本文详细介绍了Uniapp结合NFC技术开发门禁与支付系统的实战指南,特别针对Mifare Classic卡片进行深度优化。内容涵盖环境配置、设备兼容性处理、卡片安全机制解析以及高性能读写优化策略,帮助开发者快速实现跨平台的智能硬件交互解决方案。 阅读详情

大纲

1.NodeSelectorSlot构建资源调用树

2.LogSlot和StatisticSlot采集资源的数据

3.Sentinel监听器模式的规则对象与规则管理

4.AuthoritySlot控制黑白名单权限

5.SystemSlot根据系统保护规则进行流控

3.Sentinel监听器模式的规则对象与规则管理

(1)Sentinel的规则对象

(2)Sentinel的规则管理

(1)Sentinel的规则对象

一.Sentinel中的规则其实就是配置

二.规则接口Rule和抽象父类AbstractRule及其具体实现类

一.Sentinel中的规则其实就是配置

黑白名单控制规则:例如需要设置一份配置,确定哪些请求属于黑名单、哪些请求属于白名单,那么这份配置就是黑白名单控制规则。

系统负载自适应规则:例如需要设置当CPU使用率达到90%时,系统就不再接受新请求以防止系统崩溃,那么这个90%的CPU使用率阈值就是系统负载自适应规则。

流量控制规则:例如需要设置单机QPS最高为100,那么这个单机限流100QPS便是流量控制规则。

熔断降级规则:例如需要设置当错误比例在1秒内超过10次时,系统自动触发熔断降级,那么这个1秒内超过10次的错误比例就是熔断降级规则。

二.规则接口Rule和抽象父类AbstractRule及其具体实现类

首先规则与资源是紧密关联的,规则会对资源起作用,因此规则接口Rule需要一个获取资源的方法getResource()。

然后每一条具体的规则都应继承抽象父类AbstractRule并具备三个字段:规则id、资源name以及针对来源limitApp。其中针对来源指的是诸如黑名单值、白名单值等,默认是default。

//Base interface of all rules.
public interface Rule {
    //Get target resource of this rule.
    //获取当前规则起作用的目标资源
    String getResource();
}

//Abstract rule entity. AbstractRule是实现了规则接口Rule的抽象规则类
public abstract class AbstractRule implements Rule {
    //rule id. 规则id
    private 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 default, which means allowing all origin apps.
    //For authority rules, multiple origin name can be separated with comma (',').
    private String limitApp;
    
    public Long getId() {
        return id;
    }
    
    public AbstractRule setId(Long id) {
        this.id = id;
        return this;
    }
    
    @Override
    public String getResource() {
        return resource;
    }
    
    public AbstractRule setResource(String resource) {
        this.resource = resource;
        return this;
    }
    
    public String getLimitApp() {
        return limitApp;
    }
    
    public AbstractRule setLimitApp(String limitApp) {
        this.limitApp = limitApp;
        return this;
    }
    ...
}

//Authority rule is designed for limiting by request origins.
public class AuthorityRule extends AbstractRule {
    ...
}

public class SystemRule extends AbstractRule {
    ...
}

public class FlowRule extends AbstractRule {
    ...
}

public class DegradeRule extends AbstractRule {
    ...
}

(2)Sentinel的规则管理

一.PropertyListener<T>监听器接口及其实现类

二.SentinelProperty监听器接口管理所有PropertyListener<T>子类

三.DynamicSentinelProperty会触发监听器PropertyListener<T>的回调

一.PropertyListener<T>监听器接口及其实现类

为了感知规则Rule的变化,需要一个负责监听规则变化的类,也就是需要一个监听器来监听规则Rule的变化,这个监听器就是PropertyListener<T>。

PropertyListener<T>是一个接口,它定义了两个方法:方法一是首次加载规则时触发的回调方法configLoad(),方法二是规则变更时触发的回调方法configUpdate()。

PropertyListener<T>接口使用了泛型T而不是规则接口Rule来定义,是因为除了规则的变化需要监听器监听外,还有其他场景也需要监听。

PropertyListener<T>接口的具体实现类有:

AuthorityRuleManager.RulePropertyListener
FlowRuleManager.FlowPropertyListener
DegradeRuleManager.RulePropertyListener
SystemRuleManager.SystemPropertyListener
//This class holds callback method when SentinelProperty.updateValue(Object) need inform the listener.
public interface PropertyListener<T> {
    //Callback method when {@link SentinelProperty#updateValue(Object)} need inform the listener.
    //规则变更时触发的回调方法
    void configUpdate(T value);

    //The first time of the {@code value}'s load.
    //首次加载规则时触发的回调方法
    void configLoad(T value);
}

//Manager for authority rules.
public final class AuthorityRuleManager {
    //key是资源名称,value是资源对应的规则
    private static volatile Map<String, Set<AuthorityRule>> authorityRules = new ConcurrentHashMap<>();
    //饿汉式单例模式实例化黑白名单权限控制规则的监听器对象
    private static final RulePropertyListener LISTENER = new RulePropertyListener();
    //监听器对象的管理器
    private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
    
    static {
        //将黑白名单权限控制规则的监听器对象添加到DynamicSentinelProperty中
        currentProperty.addListener(LISTENER);
    }
    ...
    
    private static class RulePropertyListener implements PropertyListener<List<AuthorityRule>> {
        @Override
        public synchronized void configLoad(List<AuthorityRule> value) {
            authorityRules = loadAuthorityConf(value);
            RecordLog.info("[AuthorityRuleManager] Authority rules loaded: {}", authorityRules);
        }
        
        @Override
        public synchronized void configUpdate(List<AuthorityRule> conf) {
            authorityRules = loadAuthorityConf(conf);
            RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
        }
        
        private Map<String, Set<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
            Map<String, Set<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
            if (list == null || list.isEmpty()) {
                return newRuleMap;
            }
            for (AuthorityRule rule : list) {
                if (!isValidRule(rule)) {
                    RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: {}", rule);
                    continue;
                }
                if (StringUtil.isBlank(rule.getLimitApp())) {
                    rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
                }
                String identity = rule.getResource();
                Set<AuthorityRule> ruleSet = newRuleMap.get(identity);
                //putIfAbsent
                if (ruleSet == null) {
                    ruleSet = new HashSet<>();
                    ruleSet.add(rule);
                    newRuleMap.put(identity, ruleSet);
                } else {
                    //One resource should only have at most one authority rule, so just ignore redundant rules.
                    RecordLog.warn("[AuthorityRuleManager] Ignoring redundant rule: {}", rule.toString());
                }
            }
            return newRuleMap;
        }
    }
    ...
}

public class FlowRuleManager {
    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();
    }
    ...
    
    private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {
        @Override
        public 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);
        }
        
        @Override
        public 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 DegradeRuleManager {
    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>> {
        ...
        @Override
        public void configUpdate(List<DegradeRule> conf) {
            reloadFrom(conf);
            RecordLog.info("[DegradeRuleManager] Degrade rules has been updated to: {}", ruleMap);
        }
        
        @Override
        public void configLoad(List<DegradeRule> conf) {
            reloadFrom(conf);
            RecordLog.info("[DegradeRuleManager] Degrade rules loaded: {}", ruleMap);
        }
        ...
    }
    ...
}

二.SentinelProperty监听器接口管理所有PropertyListener<T>子类

为了在创建规则时回调configLoad()方法初始化规则配置,以及在规则变更时回调configUpdate()方法通知到所有监听者,需要一个类来管理所有监听器,比如将所有监听器添加到集合中。当配置发生变化时,就可以遍历监听器集合然后调用回调方法进行处理。

其实就是使用监听器模式或观察者模式,创建一个实现了SentinelProperty接口的类,专门负责管理所有实现了PropertyListener<T>接口的监听器。

其中SentinelProperty接口如下所示:

//This class holds current value of the config, 
//and is responsible for informing all PropertyListeners added on this when the config is updated.
//Note that not every updateValue(Object newValue) invocation should inform the listeners, 
//only when newValue is not Equals to the old value, informing is needed.
public interface SentinelProperty<T> {
    //添加监听者
    //Add a PropertyListener to this SentinelProperty.
    //After the listener is added, updateValue(Object) will inform the listener if needed.
    //This method can invoke multi times to add more than one listeners.
    void addListener(PropertyListener<T> listener);

    //移除监听者
    //Remove the PropertyListener on this. 
    //After removing, updateValue(Object) will not inform the listener.
    void removeListener(PropertyListener<T> listener);

    //当监听值有变化时,调用此方法进行通知
    //Update the newValue as the current value of this property and inform all
    //PropertyListeners added on this only when new value is not Equals to the old value.
    boolean updateValue(T newValue);
}

三.DynamicSentinelProperty会触发监听器PropertyListener<T>的回调

DynamicSentinelProperty会使用写时复制集合CopyOnWriteArraySet来存储监听器,当DynamicSentinelProperty添加监听器或者更新新值时,便会触发执行PropertyListener<T>接口的两个回调方法。

具体就是:当执行DynamicSentinelProperty的addListener()方法添加监听器时,会将监听器保存到DynamicSentinelProperty的写时复制集合CopyOnWriteArraySet中,并且回调监听器的configLoad()方法来初始化规则配置。由于监听器监听的是规则,而规则又是和资源绑定的,所以初始化就是将资源和规则绑定到一个Map中:即形如Map<String resourcename, Set<Rule>>这样的Map。

当执行DynamicSentinelProperty的updateValue()方法更新规则配置时,则会遍历所有监听器并调用每个监听器的configUpdate()方法进行更新,也就是更新Map<String resourcename, Set<Rule>>这种Map里的value。

public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
    protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();
    private T value = null;
    
    public DynamicSentinelProperty() {
    
    }
    
    public DynamicSentinelProperty(T value) {
        super();
        this.value = value;
    }
    
    //添加监听器到集合
    @Override
    public void addListener(PropertyListener<T> listener) {
        listeners.add(listener);
        //回调监听器的configLoad()方法初始化规则配置
        listener.configLoad(value);
    }
    
    //移除监听器
    @Override
    public void removeListener(PropertyListener<T> listener) {
        listeners.remove(listener);
    }
    
    //更新值
    @Override
    public boolean updateValue(T newValue) {
        //如果值没变化,直接返回
        if (isEqual(value, newValue)) {
            return false;
        }
        RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);
        value = newValue;
        //如果值发生了变化,则遍历监听器,回调监听器的configUpdate()方法更新对应的值
        for (PropertyListener<T> listener : listeners) {
            listener.configUpdate(newValue);
        }
        return true;
    }
    ...
}

(3)总结

一.PropertyListener<T>

PropertyListener<T>是一个泛型接口,用于监听配置变更。它包含两个方法:configUpdate()方法和configLoad()方法。

PropertyListener的configUpdate()方法在配置发生变化时触发,PropertyListener的configLoad()方法在首次加载配置时触发。通过实现PropertyListener<T>接口,可以实现不同类型的监听器,例如FlowPropertyListener等。

二.SentinelProperty

SentinelProperty是一个用于管理PropertyListener监听器的接口,它提供了添加、移除和更新监听器的方法。

添加监听器可调用SentinelProperty实现类的addListener()方法实现添加,配置变更可调用SentinelProperty实现类的updateValue()方法通知监听器。Sentinel提供了默认的SentinelProperty实现:DynamicSentinelProperty。

4.AuthoritySlot控制黑白名单权限

(1)黑白名单权限控制规则的配置Demo

(2)AuthoritySlot验证黑白名单权限控制规则

(1)黑白名单权限控制规则的配置Demo

一.配置黑白名单权限控制规则的过程

二.AuthorityRuleManager初始化和加载黑白名单权限控制规则详情

一.配置黑白名单权限控制规则的过程

首先创建一个AuthorityRule规则对象,然后设置三个关键要素:通过setStrategy()方法设置规则是黑名单还是白名单、通过setResource()方法设置规则绑定到哪个资源、通过setLimitApp()方法设置限制哪些来源,最后调用AuthorityRuleManager的loadRules()方法加载此规则。所以黑白名单权限规则是通过AuthorityRuleManager类来进行管理的。

//Authority rule is designed for limiting by request origins.
//In blacklist mode, requests will be blocked when blacklist contains current origin, otherwise will pass.
//In whitelist mode, only requests from whitelist origin can pass.
public class AuthorityDemo {
    private static final String RESOURCE_NAME = "testABC";
    
    public static void main(String[] args) {
        System.out.println("========Testing for black list========");
        initBlackRules();
        testFor(RESOURCE_NAME, "appA");
        testFor(RESOURCE_NAME, "appB");
        testFor(RESOURCE_NAME, "appC");
        testFor(RESOURCE_NAME, "appE");

        System.out.println("========Testing for white list========");
        initWhiteRules();
        testFor(RESOURCE_NAME, "appA");
        testFor(RESOURCE_NAME, "appB");
        testFor(RESOURCE_NAME, "appC");
        testFor(RESOURCE_NAME, "appE");
    }
    
    private static void testFor(String resource, String origin) {
        ContextUtil.enter(resource, origin);
        Entry entry = null;
        try {
            entry = SphU.entry(resource);
            System.out.println(String.format("Passed for resource %s, origin is %s", resource, origin));
        } catch (BlockException ex) {
            System.err.println(String.format("Blocked for resource %s, origin is %s", resource, origin));
        } finally {
            if (entry != null) {
                entry.exit();
            }
            ContextUtil.exit();
        }
    }
    
    private static void initWhiteRules() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource(RESOURCE_NAME);
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        rule.setLimitApp("appA,appE");
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }
    
    private static void initBlackRules() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource(RESOURCE_NAME);
        rule.setStrategy(RuleConstant.AUTHORITY_BLACK);
        rule.setLimitApp("appA,appB");
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }
}

二.AuthorityRuleManager初始化和加载黑白名单权限控制规则详情

AuthorityRuleManager类中有一个静态变量LISTENER,该变量指向由饿汉式单例模式实例化的黑白名单权限控制规则监听器对象。

AuthorityRuleManager类有一个静态代码块,在该代码块中,会调用DynamicSentinelProperty的addListener(LISTENER)方法,将黑白名单权限控制规则的监听器对象添加到DynamicSentinelProperty。

在DynamicSentinelProperty的addListener()方法中,又会回调LISTENER的configLoad()方法初始化黑白名单权限规则。

当AuthorityDemo调用AuthorityRuleManager的loadRules()方法加载规则时,便会执行DynamicSentinelProperty的updateValue()方法,也就是会触发执行LISTENER的configUpdate()方法加载权限规则到一个map中,即执行RulePropertyListener的loadAuthorityConf()方法加载规则,从而完成黑白名单权限控制规则的加载和初始化。其中map是AuthorityRuleManager的Map<String, Set<AuthorityRule>>。

//Manager for authority rules.
public final class AuthorityRuleManager {
    //key是资源名称,value是资源对应的规则
    private static volatile Map<String, Set<AuthorityRule>> authorityRules = new ConcurrentHashMap<>();
    //饿汉式单例模式实例化黑白名单权限控制规则的监听器对象
    private static final RulePropertyListener LISTENER = new RulePropertyListener();
    //监听器对象的管理器
    private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
    
    static {
        //将黑白名单权限控制规则的监听器对象添加到DynamicSentinelProperty中
        currentProperty.addListener(LISTENER);
    }
    
    //Load the authority rules to memory.
    public static void loadRules(List<AuthorityRule> rules) {
        currentProperty.updateValue(rules);
    }
    ...
    
    //静态内部类的方式实现黑白名单权限控制规则监听器
    private static class RulePropertyListener implements PropertyListener<List<AuthorityRule>> {
        //黑名单权限控制规则初始化
        @Override
        public synchronized void configLoad(List<AuthorityRule> value) {
            authorityRules = loadAuthorityConf(value);
            RecordLog.info("[AuthorityRuleManager] Authority rules loaded: {}", authorityRules);
        }
        
        //黑名单权限控制规则变更
        @Override
        public synchronized void configUpdate(List<AuthorityRule> conf) {
            authorityRules = loadAuthorityConf(conf);
            RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
        }
        
        //加载黑白名单权限控制规则
        private Map<String, Set<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
            Map<String, Set<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
            if (list == null || list.isEmpty()) {
                return newRuleMap;
            }
            //遍历每个黑白名单权限控制规则
            for (AuthorityRule rule : list) {
                if (!isValidRule(rule)) {
                    RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: {}", rule);
                    continue;
                }
                if (StringUtil.isBlank(rule.getLimitApp())) {
                    rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
                }
                //获取黑白名单权限控制规则对应的资源名称
                String identity = rule.getResource();
                Set<AuthorityRule> ruleSet = newRuleMap.get(identity);
                //putIfAbsent
                //将黑白名单权限控制规则放到newRuleMap中
                if (ruleSet == null) {
                    ruleSet = new HashSet<>();
                    ruleSet.add(rule);
                    newRuleMap.put(identity, ruleSet);
                } else {
                    //One resource should only have at most one authority rule, so just ignore redundant rules.
                    RecordLog.warn("[AuthorityRuleManager] Ignoring redundant rule: {}", rule.toString());
                }
            }
            return newRuleMap;
        }
    }
    ...
}

public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
    protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();
    private T value = null;
    
    public DynamicSentinelProperty() {
    
    }
    
    public DynamicSentinelProperty(T value) {
        super();
        this.value = value;
    }
    
    //添加监听器到集合
    @Override
    public void addListener(PropertyListener<T> listener) {
        listeners.add(listener);
        //回调监听器的configLoad()方法初始化规则配置
        listener.configLoad(value);
    }
    
    //移除监听器
    @Override
    public void removeListener(PropertyListener<T> listener) {
        listeners.remove(listener);
    }
    
    //更新值
    @Override
    public 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();
    }
}

(2)AuthoritySlot验证黑白名单权限控制规则

在AuthoritySlot的checkBlackWhiteAuthority()方法中,首先会调用AuthorityRuleManager的getAuthorityRules()方法,从AuthorityRuleManager中获取全部黑白名单权限控制规则,然后再调用AuthorityRuleChecker的passCheck()方法根据规则验证权限。

在AuthorityRuleChecker的passCheck()方法中,首先会从当前上下文Context中获取调用源的名称,然后判断调用源不空且配置了黑白名单规则,才执行黑白名单验证逻辑。接着先通过indexOf()方法进行一次黑白名单的简单匹配,再通过split()方法分割黑白名单数组以实现精确匹配。如果调用源在名单中,再根据黑白名单策略来决定是否拒绝请求。

注意,实现黑白名单权限控制的前提条件是:每个客户端在发起请求时已将自己服务的唯一标志放到Context的origin属性里。

@Spi(order = Constants.ORDER_AUTHORITY_SLOT)
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
        //验证黑白名单权限控制规则
        checkBlackWhiteAuthority(resourceWrapper, context);
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }
    
    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }
    
    void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
        //先从AuthorityRuleManager中获取存放全部的黑白名单权限控制规则的Map
        Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
        if (authorityRules == null) {
            return;
        }
        //获取当前资源对应的黑白名单权限控制规则集合
        Set<AuthorityRule> rules = authorityRules.get(resource.getName());
        if (rules == null) {
            return;
        }
        for (AuthorityRule rule : rules) {
            //验证规则
            if (!AuthorityRuleChecker.passCheck(rule, context)) {
                throw new AuthorityException(context.getOrigin(), rule);
            }
        }
    }
}

//Rule checker for white/black list authority.
final class AuthorityRuleChecker {
    static boolean passCheck(AuthorityRule rule, Context context) {
        String requester = context.getOrigin();
        //Empty origin or empty limitApp will pass.
        //如果没设置来源,或者没限制app,那么就直接放行,不进行规则限制
        if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
            return true;
        }

        //Do exact match with origin name.
        //判断此次请求的来源是不是在limitApp里,注意这里用的是近似精确匹配,但不是绝对精确
        //比如limitApp写的是a,b,而资源名称是",b",那么就匹配不到,因为limitApp是按逗号隔开的,但资源却包含了逗号
        int pos = rule.getLimitApp().indexOf(requester);
        boolean contain = pos > -1;

        //如果近似精确匹配成功,则再进行精确匹配
        if (contain) {
            boolean exactlyMatch = false;
            String[] appArray = rule.getLimitApp().split(",");
            for (String app : appArray) {
                if (requester.equals(app)) {
                    exactlyMatch = true;
                    break;
                }
            }
            contain = exactlyMatch;
        }
        //获取策略
        int strategy = rule.getStrategy();
        //如果是黑名单,并且此次请求的来源在limitApp里,则需返回false,禁止请求
        if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
            return false;
        }
        //如果是白名单,并且此次请求的来源不在limitApp里,则也需返回false,禁止请求
        if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
            return false;
        }
        return true;
    }
    
    private AuthorityRuleChecker() {
    
    }
}

(3)总结

一.黑白名单权限验证规则涉及的核心类

二.黑白名单权限验证的处理逻辑

三.Sentinel监听器模式的处理逻辑

一.黑白名单权限验证规则涉及的核心类

首先是黑白名单管理器AuthorityRuleManager,调用方直接调用该类的loadRules()方法来通知监听器规则的变更。

然后是黑白名单监听器RulePropertyListener,它实现了PropertyListener接口,负责监听和管理黑白名单的规则变化。

二.黑白名单权限验证的处理逻辑

首先通过AuthorityRuleManager获取全部黑白名单权限控制规则,然后循环遍历这些权限控制规则逐一验证是否匹配。

这里需要注意:来源是从Context里获取的,也就是Context的getOrigin()方法。因此在进行黑白名单权限规则控制时,需要先定义好一个origin。这个origin可以是userId,也可以是IP地址,还可以是项目名称等。

此外,规则里的limitApp字段是字符串,多个时需要使用逗号隔开,然后在验证环节先通过indexOf()方法近似匹配,匹配上之后再通过split()方法转成数组进行精确匹配。

三.Sentinel监听器模式的处理逻辑

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

角色一:监听器PropertyListener<T>

角色二:监听器管理器SentinelProperty<T>

角色三:规则管理器RuleManager

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

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

5.SystemSlot根据系统保护规则进行流控

(1)系统保护规则SystemRule的配置Demo

(2)SystemRuleManager加载规则和获取系统信息

(3)SystemSlot根据系统保护规则进行流控

(1)系统保护规则SystemRule的配置Demo

系统规则类SystemRule包含了以下几个指标:highestSystemLoad、highestCpuUsage、QPS、avgRt、maxThread。

当需要限制系统的这些指标时,可以创建一个SystemRule对象并设置对应的阈值,然后通过调用SystemRuleManager的loadRules()方法,加载系统保护规则设置的阈值到SystemRuleManager。

//Sentinel System Rule makes the inbound traffic and capacity meet. 
//It takes average RT, QPS and thread count of requests into account. 
//And it also provides a measurement of system's load, but only available on Linux.
//We recommend to coordinate highestSystemLoad, qps, avgRt and maxThread to make sure your system run in safety level.
//To set the threshold appropriately, performance test may be needed.
public class SystemRule extends AbstractRule {
    //对应Dashboard上阈值类型为LOAD的值,代表系统最高负载值,默认为-1,只有大于等于0才生效
    private double highestSystemLoad = -1;
    //对应Dashboard上阈值类型为CPU使用率的值,代表系统最高CPU使用率,取值是[0,1]之间,默认为-1,只有大于等于0才生效
    private double highestCpuUsage = -1;
    //对应Dashboard上阈值类型为为入口QPS的值,代表限流的阈值,默认为-1,只有大于0才生效
    private double qps = -1;
    //对应Dashboard上阈值类型为为RT的值,代表系统的平均响应时间,默认为-1,只有大于0才生效
    private long avgRt = -1;
    //对应Dashboard上阈值类型为线程数的值,代表系统允许的最大线程数,默认为-1,只有大于0才生效
    private long maxThread = -1;
    ...
}

public class SystemGuardDemo {
    private static AtomicInteger pass = new AtomicInteger();
    private static AtomicInteger block = new AtomicInteger();
    private static AtomicInteger total = new AtomicInteger();
    private static volatile boolean stop = false;
    private static final int threadCount = 100;
    private static int seconds = 60 + 40;
    
    public static void main(String[] args) throws Exception {
        //启动线程定时输出信息
        tick();
        //初始化系统保护规则
        initSystemRule();
        //模拟有100个线程在访问系统
        for (int i = 0; i < threadCount; i++) {
            Thread entryThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        Entry entry = null;
                        try {
                            entry = SphU.entry("methodA", EntryType.IN);
                            pass.incrementAndGet();
                            try {
                                TimeUnit.MILLISECONDS.sleep(20);
                            } catch (InterruptedException e) {
                                // ignore
                            }
                        } catch (BlockException e1) {
                            block.incrementAndGet();
                            try {
                                TimeUnit.MILLISECONDS.sleep(20);
                            } catch (InterruptedException e) {
                                // ignore
                            }
                        } catch (Exception e2) {
                            // biz exception
                        } finally {
                            total.incrementAndGet();
                            if (entry != null) {
                                entry.exit();
                            }
                        }
                    }
                }
            });
            entryThread.setName("working-thread");
            entryThread.start();
        }
    }
    
    private static void initSystemRule() {
        List<SystemRule> rules = new ArrayList<SystemRule>();
        SystemRule rule = new SystemRule();
        //最大负载是3
        rule.setHighestSystemLoad(3.0);
        //最大CPU使用率是60%
        rule.setHighestCpuUsage(0.6);
        //请求的平均响应时间最大是10ms
        rule.setAvgRt(10);
        //最大的QPS是20
        rule.setQps(20);
        //最大的工作线程数是10
        rule.setMaxThread(10);
        rules.add(rule);
        //加载系统保护规则设置的阈值到SystemRuleManager中
        SystemRuleManager.loadRules(Collections.singletonList(rule));
    }
    
    private static void tick() {
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-task");
        timer.start();
    }
    
    static class TimerTask implements Runnable {
        @Override
        public void run() {
            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 + ", " + TimeUtil.currentTimeMillis() + ", total:"
                    + oneSecondTotal + ", pass:"
                    + oneSecondPass + ", block:" + oneSecondBlock);
                if (seconds-- <= 0) {
                    stop = true;
                }
            }
            System.exit(0);
        }
    }
}

(2)SystemRuleManager加载规则和获取系统信息

一.在加载系统保护规则设置的阈值到本地方面

SystemRuleManager会通过loadRules()方法加载系统保护规则的阈值,即调用DynamicSentinelProperty的updateValue()方法通知监听器更新规则。此时会触发执行监听器SystemPropertyListener的configUpdate()方法,从而执行SystemRuleManager的loadSystemConf()方法更新本地规则阈值。

二.在获取系统信息方面

SystemRuleManager初始化时会启动一个线程SystemPropertyListener,每隔1秒定时获取系统的Load、CPU使用率等信息,这样后续便可以通过SystemPropertyListener获取系统负载等信息。

public final class SystemRuleManager {
    //系统保护规则中的5个阈值:Load、CPU使用率、QPS、最大RT、最大线程数
    private static volatile double highestSystemLoad = Double.MAX_VALUE;
    private static volatile double highestCpuUsage = Double.MAX_VALUE;
    private static volatile double qps = Double.MAX_VALUE;
    private static volatile long maxRt = Long.MAX_VALUE;
    private static volatile long maxThread = Long.MAX_VALUE;
    ...
    //标记系统流控功能是否开启
    private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);
    //定时获取系统状态信息(负载和CPU使用率)的线程
    private static SystemStatusListener statusListener = null;
    //饿汉式单例模式实例化系统保护规则的监听器对象
    private final static SystemPropertyListener listener = new SystemPropertyListener();
    //监听器对象的管理器
    private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();
    private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-system-status-record-task", true));
    
    static {
        checkSystemStatus.set(false);
        //启动定时任务获取系统的Load、CPU负载等信息
        statusListener = new SystemStatusListener();
        scheduler.scheduleAtFixedRate(statusListener, 0, 1, TimeUnit.SECONDS);
        //添加监听器
        currentProperty.addListener(listener);
    }
    
    //Load SystemRules, former rules will be replaced.
    public static void loadRules(List<SystemRule> rules) {
        currentProperty.updateValue(rules);
    }
    
    static class SystemPropertyListener extends SimplePropertyListener<List<SystemRule>> {
        @Override
        public synchronized void configUpdate(List<SystemRule> rules) {
            restoreSetting();
            if (rules != null && rules.size() >= 1) {
                for (SystemRule rule : rules) {
                    //加载系统保护规则的阈值到本地
                    loadSystemConf(rule);
                }
            } else {
                checkSystemStatus.set(false);
            }
            ...
        }
    }
    
    //加载系统保护规则的阈值到本地
    public static void loadSystemConf(SystemRule rule) {
        boolean checkStatus = false;
        if (rule.getHighestSystemLoad() >= 0) {
            highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());
            highestSystemLoadIsSet = true;
            checkStatus = true;
        }
        if (rule.getHighestCpuUsage() >= 0) {
            if (rule.getHighestCpuUsage() > 1) {
                RecordLog.warn(String.format("[SystemRuleManager] Ignoring invalid SystemRule: " + "highestCpuUsage %.3f > 1", rule.getHighestCpuUsage()));
            } else {
                highestCpuUsage = Math.min(highestCpuUsage, rule.getHighestCpuUsage());
                highestCpuUsageIsSet = true;
                checkStatus = true;
            }
        }
        if (rule.getAvgRt() >= 0) {
            maxRt = Math.min(maxRt, rule.getAvgRt());
            maxRtIsSet = true;
            checkStatus = true;
        }
        if (rule.getMaxThread() >= 0) {
            maxThread = Math.min(maxThread, rule.getMaxThread());
            maxThreadIsSet = true;
            checkStatus = true;
        }
        if (rule.getQps() >= 0) {
            qps = Math.min(qps, rule.getQps());
            qpsIsSet = true;
            checkStatus = true;
        }
        checkSystemStatus.set(checkStatus);
    }
    ...
}

public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
    protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();
    private T value = null;
    ...
    //添加监听器到集合
    @Override
    public void addListener(PropertyListener<T> listener) {
        listeners.add(listener);
        //回调监听器的configLoad()方法初始化规则配置
        listener.configLoad(value);
    }
    
    //更新值
    @Override
    public 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 class SystemStatusListener implements Runnable {
    volatile double currentLoad = -1;
    volatile double currentCpuUsage = -1;
    volatile long processCpuTime = 0;
    volatile long processUpTime = 0;
    ...
    @Override
    public void run() {
        try {
            OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
            currentLoad = osBean.getSystemLoadAverage();
            double systemCpuUsage = osBean.getSystemCpuLoad();

            //calculate process cpu usage to support application running in container environment
            RuntimeMXBean runtimeBean = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class);
            long newProcessCpuTime = osBean.getProcessCpuTime();
            long newProcessUpTime = runtimeBean.getUptime();
            int cpuCores = osBean.getAvailableProcessors();
            long processCpuTimeDiffInMs = TimeUnit.NANOSECONDS.toMillis(newProcessCpuTime - processCpuTime);
            long processUpTimeDiffInMs = newProcessUpTime - processUpTime;
            double processCpuUsage = (double) processCpuTimeDiffInMs / processUpTimeDiffInMs / cpuCores;
            processCpuTime = newProcessCpuTime;
            processUpTime = newProcessUpTime;
            currentCpuUsage = Math.max(processCpuUsage, systemCpuUsage);
            if (currentLoad > SystemRuleManager.getSystemLoadThreshold()) {
                writeSystemStatusLog();
            }
        } catch (Throwable e) {
            RecordLog.warn("[SystemStatusListener] Failed to get system metrics from JMX", e);
        }
    }
    
    public double getSystemAverageLoad() {
        return currentLoad;
    }
    
    public double getCpuUsage() {
        return currentCpuUsage;
    }
    ...
}

(3)SystemSlot根据系统保护规则进行流控

SystemSlot会根据当前系统的实际情况,判断是否需要对请求进行限流,也就是通过调用SystemRuleManager的checkSystem()方法来进行检查。

在SystemRuleManager的checkSystem()方法中:

一.首先通过checkSystemStatus.get()判断系统保护功能是否开启

开启的入口就是:

->SystemRuleManager.loadRules()方法
->DynamicSentinelProperty.updateValue()方法
->SystemPropertyListener.configUpdate()方法
->SystemRuleManager.loadSystemConf()方法

二.接着通过Constants.ENTRY_NODE获取如QPS、threadNum等数据

Constants.ENTRY_NODE其实就是ClusterNode。在StatisticSlot的entry()方法中,会对Constants.ENTRY_NODE进行统计,所以可以通过Constants.ENTRY_NODE获取QPS、threadNum等数据。

三.然后采取BBR算法来检查系统负载是否超过系统保护规则的阈值

BBR是Google开发的一种拥塞控制算法,主要用来解决网络拥塞问题。SystemRuleManager的checkBbr()方法的目的是在系统负载较高的情况下,通过限制并行线程数来防止系统过载。

简单来说就是:检查当前线程数是否大于(每秒最大成功请求数 * 最小响应时间 / 1000)。如果大于这个值,说明系统可能出现拥塞,要返回false,否则返回true。

四.最后判断CPU使用率是否超系统保护规则的阈值

系统负载和CPU使用率是通过SystemStatusListener获取的。

@Spi(order = Constants.ORDER_SYSTEM_SLOT)
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
        //检查系统保护规则
        SystemRuleManager.checkSystem(resourceWrapper, count);
        //执行下一个ProcessorSlot
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }
    
    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }
}

public final class SystemRuleManager {
    //系统保护规则中的5个阈值:Load、CPU使用率、QPS、最大RT、最大线程数
    private static volatile double highestSystemLoad = Double.MAX_VALUE;
    private static volatile double highestCpuUsage = Double.MAX_VALUE;
    private static volatile double qps = Double.MAX_VALUE;
    private static volatile long maxRt = Long.MAX_VALUE;
    private static volatile long maxThread = Long.MAX_VALUE;
    ...
    //标记系统流控功能是否开启
    private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);
    //定时获取系统状态信息(负载和CPU使用率)的线程
    private static SystemStatusListener statusListener = null;
    //监听器对象的管理器
    private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();
    ...
    //Apply SystemRule to the resource. Only inbound traffic will be checked.
    public static void checkSystem(ResourceWrapper resourceWrapper, int count) throws BlockException {
        //资源为空则直接返回
        if (resourceWrapper == null) {
            return;
        }
        //Ensure the checking switch is on.
        //判断系统流控功能是否开启,如果没开启则直接返回
        if (!checkSystemStatus.get()) {
            return;
        }
        //for inbound traffic only
        //判断资源的流量是否为入口流量,如果不是IN,则直接返回
        //也就是说Sentinel系统保护规则限流只对入口流量生效,如果类型为OUT则直接返回
        if (resourceWrapper.getEntryType() != EntryType.IN) {
            return;
        }
        //total qps
        //获取当前qps,如果当前qps大于系统保护规则SystemRule配置的阈值,则抛出SystemBlockException异常
        double currentQps = Constants.ENTRY_NODE.passQps();
        if (currentQps + count > qps) {
            throw new SystemBlockException(resourceWrapper.getName(), "qps");
        }
        //total thread
        //获取当前线程数,如果当前线程数大于系统保护规则SystemRule配置的阈值,则抛出SystemBlockException异常
        int currentThread = Constants.ENTRY_NODE.curThreadNum();
        if (currentThread > maxThread) {
            throw new SystemBlockException(resourceWrapper.getName(), "thread");
        }
        //如果当前请求的平均响应时间大于系统保护规则SystemRule配置的阈值,则抛出SystemBlockException异常
        double rt = Constants.ENTRY_NODE.avgRt();
        if (rt > maxRt) {
            throw new SystemBlockException(resourceWrapper.getName(), "rt");
        }
        //load. BBR algorithm.
        //如果当前系统负载大于系统保护规则SystemRule配置的负载,则采取BBR算法验证,验证不通过则抛出SystemBlockException异常
        if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
            if (!checkBbr(currentThread)) {
                throw new SystemBlockException(resourceWrapper.getName(), "load");
            }
        }
        //cpu usage
        //判断当前CPU使用率是否大于系统保护规则SystemRule配置的阈值,如果大于则抛出SystemBlockException异常
        if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
            throw new SystemBlockException(resourceWrapper.getName(), "cpu");
        }
    }

    //BBR(Bottleneck Bandwidth and Round-trip propagation time)是Google开发的一种拥塞控制算法;
    //BBR主要用来解决网络拥塞问题;
    
    //checkBbr()方法的目的是在系统负载较高的情况下,通过限制并行线程数来防止系统过载;
    //简单来说就是:
    //检查当前线程数是否大于(每秒最大成功请求数 * 最小响应时间 / 1000);
    //如果大于这个值,说明系统可能出现拥塞,需要返回false,否则返回true;
    
    //具体来说就是:
    //首先检查当前线程数是否大于1,如果不是,则直接返回true,表示通过BBR检查;
    //如果当前线程数大于1,那么检查当前线程数是否大于:
    //(Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000);
    //这里的maxSuccessQps()是每秒最大成功请求数,minRt()是最小响应时间;
    //如果当前线程数大于这个计算值,那么返回false,表示未通过BBR检查;否则,返回true;

    //举个例子:
    //假设currentThread为 5,maxSuccessQps()为 10,minRt()为200;
    //那么计算值为(10 * 200) / 1000 = 2;
    //因为currentThread大于计算值,所以返回false,表示未通过BBR检查;
    private static boolean checkBbr(int currentThread) {
        //检查当前线程数是否大于(每秒最大成功请求数 * 最小响应时间 / 1000)
        //如果大于这个值,说明系统可能出现拥塞,需要返回false,否则返回true
        if (currentThread > 1 && currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
            return false;
        }
        return true;
    }

    public static double getCurrentSystemAvgLoad() {
        return statusListener.getSystemAverageLoad();
    }
    
    public static double getCurrentCpuUsage() {
        return statusListener.getCpuUsage();
    }
    ...
}

@Spi(order = Constants.ORDER_STATISTIC_SLOT)
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
        ...
        if (resourceWrapper.getEntryType() == EntryType.IN) {
            //Add count for global inbound entry node for global statistics.
            Constants.ENTRY_NODE.increaseThreadNum();
            Constants.ENTRY_NODE.addPassRequest(count);
        }
        ...
    }
    ...
}

public final class Constants {
    ...
    //Global statistic node for inbound traffic. Usually used for {@code SystemRule} checking.
    public final static ClusterNode ENTRY_NODE = new ClusterNode(TOTAL_IN_RESOURCE_NAME, ResourceTypeConstants.COMMON);
    ...
}

(4)总结

一.SystemSlot的使用和处理流程

二.Sentinel监听器模式的处理逻辑

一.SystemSlot的使用和处理流程

在使用SystemSlot前,需要先定义系统保护规则,设置相应的阈值,然后通过SystemRuleManager加载系统保护规则SystemRule。当请求进入SystemSlot时,会检查系统性能数据是否满足规则中的阈值。如果满足,则请求可以继续执行。如果不满足,则请求将被限流,也就是抛出SystemBlockException异常。

二.Sentinel监听器模式的处理逻辑

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

角色一:监听器PropertyListener<T>

角色二:监听器管理器SentinelProperty<T>

角色三:规则管理器RuleManager

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

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

Ghidra逆向分析实战:从安装JDK到解析STM32的bin文件(附SVD插件配置) 本文详细介绍了使用Ghidra进行STM32固件逆向分析的完整流程,从JDK安装配置到SVD插件的应用。通过实战案例展示如何解析bin文件、恢复符号信息以及利用SVD插件识别外设寄存器,帮助开发者高效完成嵌入式设备的逆向工程任务。 阅读详情

相关推荐

<202505高项>《【VIP合集】 软考高项(第四版)十大知识域、五大过程组、ITTO输入输出工具、项目绩效域》

软考高项(第四版)十大知识域、五大过程组、ITTO输入输出工具、项目绩效域

Else 的博客 1854

Sentinel源码解析-ProcessorSlot具体

判断上一次请求重试的时间距离当前时间有没有过重试间隔这里首先把熔断器状态成OPEN变更HALF_OPEN如果请求发生异常,则会把状态再从HALF_OPEN变成OPEN状态注:这里我们只看到了断路器状态从HALF_OPEN变成OPEN,和从OPEN变成HALF_OPEN,那么从CLOSE状态变成OPEN状态和从HALF_OEPN状态变成CLOSE状态我们还没看到,我们后续看下方法。

ZXMSH的博客 893

【实战指南】基于YOLOv11的肺结节检测系统开发与优化(LUNA2016数据集)

本文提供了基于YOLOv11模型和LUNA2016数据集开发肺结节检测系统的完整实战指南。详细介绍了从环境搭建、数据预处理、模型训练与优化,到构建Web应用系统的全流程,旨在帮助开发者快速掌握医疗影像AI项目的核心开发技能。

weixin_29031057的博客 140

spring cloud alibaba 学习(十六)Sentinel功能插槽ProcessorSlot

目录前言一、ProcessorSlotChain 初始化二、ProcessorSlotChain 使用三、ProcessorSlot 实现类1、NodeSelectorSlot2、ClusterBuilderSlot3、LogSlot4、AuthoritySlot5、SystemSlot 前言 sentinel 可以对资源也就是接口配置一些规则,各种规则检查的功能都继承ProcessorSlot,sentinel 会将不同的 ProcessorSlot 组装为一个责任链,当请求来的时候,会对资源的规..

码易的博客 737

超详细!新手必看STM32-DMA配置与实验(串口使用DMA发送数据)

DMA就是:直接存储器访问。DMA传输数据从一个地址空间复制到另一个地址空间,提供在外设和存储器或者存储器和存储器之间的高速数据传输。

qq_1033275663的博客 2万+

Emuelec系统安装全流程:超详细版指南(从零开始)

手把手带你完成emuelec系统的完整安装,从基础准备到系统配置一步不落,适合新手入门。深入解析emuelec在各类设备上的部署技巧,让复古游戏体验更流畅。

weixin_42594419的博客 1970

16. 自定义 ProcessorSlot 实现开关降级

开关降级在我们公司的电商项目中是每个微服务都必须支持的一项功能,主要用于活动期间、每日流量高峰期间、主播带货期间关闭一些无关紧要功能,降低数据库的压力。 开关降级实现起来很简单,例如,我们可以使用 Spring AOP 或者动态代理模式拦截目标方法的执行,在方法执行之前,先根据 key 从 Redis 读取 value,如果 value 是 true,则不执行目标方法,直接响应服务降级。这种方式付出的性能损耗就只有一次 redis 的 get 操作,如果不想每个请求都读 Redis 缓存,也可以通过动态配

八荒六合唯我独尊 542

DC-DC 转换器中的压电谐振器:当前状态和限制

在功率转换器应用中,居里温度较低的谐振器的 k 2和 Q M显着降低会导致效率降低,需要仔细的热处理。由于使用 PR 的功率转换器在不同的条件下运行,包括温度范围、电压偏置和功率电平,因此不可避免地会出现非线性效应,必须妥善解决这一问题,以免影响转换器的性能。等效电路模型(Butterworth Van-Dyke 或 BVD 模型)将压电谐振器在其机械谐振附近的电响应转换为一个简单电路,该电路由与由电极形成的输入电容 C 0并联的串联 RLC 动分支组成,见图图 1 还显示了阻抗与频率的关系。

2301_79716471的博客 1297

ProcessSlot构建流程分析

首先初始化Builder,负责管控责任链整体,Sentinel要求Builder只能存在一个,而且是外部系统优先原则,因此,我们可以自己编写一个以接口为名称的SPI文件来替代默认的 DefaultSlotChainBuilder。然后通过Builder初始化完整的责任链,这里仍然是通过SPI机制进行初始化,因此也可以额外扩展,比如外部系统自定义一个Slot且插入到完整责任链当中,这里值得注意的是责任链是有顺序的,如果顺序没指定正确,则可能造成意想不到的效果。顺序可以通过@Spi注解的order属性。

weixin_51918722的博客 1097

LVGL 蜂巢气泡菜单(Hex Menu)

h。

摸鱼专家 176

【python爬虫--爬取电商商品及对其销量、好差评等数据进行分析】Python爬虫爬取电商商品及大数据分析-实战

选择目标电商网站:这里选择爬取两个不同电商网站(比如京东、淘宝)上的商品数据,包括商品名称、价格、销量和评价信息。编写爬虫:通过爬虫获取商品信息并保存数据。数据清洗与对齐:将两个电商网站抓取到的数据进行统一清洗和对齐。数据分析:进行销量分析、好评与差评分析等。

Dreams°的博客 4012

【RuoYi-Cloud-Plus】学习笔记 07 - Sentinel(二)Node & ProcessorSlotChain 总览与知识整理

关于 Node 节点以及 Slot Chain 处理器链的知识整理。

MichelleChung的星球 3325

KUKA机器人在自动运行时,动作不流畅,每个点位都有停顿的解决办法

KUKA机器人在自动运行时,动作不流畅,每个点位都有停顿的解决办法

Robot_PLC_HMI_Automation 4577

Sentinel源码—ProcessorSlot的执行过程

其实Context对象的属性entranceNode就代表了一棵资源调用树。首先,在调用ContextUtil的trueEnter()方法创建Context对象实例时,便会创建一个EntranceNode对象并赋值给Context的entranceNode属性,以及调用Constants.ROOT的addChild()方法,将这个EntranceNode对象放入Constants.ROOT的childList列表中。

sdgfafg_25的博客 854

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Sentinel源码—3.ProcessorSlot的执行过程

Sentinel源码—3.ProcessorSlot的执行过程大纲1.NodeSelectorSlot构建资源调用树2.LogSlot和StatisticSlot采集资源的数据3.Sentinel监听器模式的规则对象与规则管理4.AuthoritySlot控制黑白名单权限5.SystemSlot根据系统保护规则进行流控 1.NodeSelectorSlot构建资源调用树(1)Entry的处理链的执行入口(2)NodeSelectorSlot的源码(3)Context对象中存储的资源调用树总结 (1)Entr

weixin_55010563的博客 556

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Sentinel源码—3.ProcessorSlot的执行过程一

由于ClusterNode会统计某个资源在全部Context下的调用数据,它是按照集群中的资源维度进行调用数据统计的,而StatisticSlot的entry()调用DefaultNode的方法统计单机下的资源时,会顺便调用ClusterNode的方法来统计集群下的资源调用,所以通过ClusterNode就可以获取集群中某个资源的调用数据。每当一个线程处理包含某些资源的接口请求时,会调用SphU的entry()方法去创建并管控该接口中涉及的Entry资源访问对象。

mjunz的博客 1035

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Sentinel源码—ProcessorSlot的执行过程(二)

一.PropertyListener<T>PropertyListener<T>是一个泛型接口,用于监听配置变更。它包含两个方法:configUpdate()方法和configLoad()方法。PropertyListener的configUpdate()方法在配置发生变化时触发,PropertyListener的configLoad()方法在首次加载配置时触发。通过实现PropertyListener<T>接口,可以实现不同类型的监听器,例如FlowPropertyListener等。

sdgfafg_25的博客 1084
上一篇: Sentinel源码—3.ProcessorSlot的执行过程一
下一篇: Sentinel源码—4.FlowSlot实现流控的原理一
东阳马生架构
博客等级 码龄2年 2727粉丝 187原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值