美文网首页程序员
Sentinel之Slots插槽源码分析系统规则(三)

Sentinel之Slots插槽源码分析系统规则(三)

作者: 橘子_好多灰 | 来源:发表于2018-12-30 18:22 被阅读0次

    一、引子

    前面的介绍过插槽链的NodeSelectorSlot和ClusterBuilderSlot,见Sentinel之Slots插槽源码分析(一)这篇文章。
    还介绍了LogSlot和StatisticSlot,见Sentinel之Slots插槽源码分析(二)这篇文章。

    接下来我们开始分析SystemSlot。

    二、SystemSlot

    SystemSlot主要是用来系统规则的检查,包括平均RT,qps,线程数,系统负载(只是针对linux系统)。

    下面具体分析SystemSlot是如何实现系统检查的。

    2.1 SystemSlot类

    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);
            fireEntry(context, resourceWrapper, node, count, prioritized, args);
        }
    
        @Override
        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
            fireExit(context, resourceWrapper, count, args);
        }
    
    }
    

    可以看到调用了SystemRuleManager.的checkSystem方法,到SystemRuleManager类中去。

    2.2 SystemRuleManager类

    先看定义的变量信息:

        private static volatile double highestSystemLoad = 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;
        /**
         * mark whether the threshold are set by user.
         */
        private static volatile boolean highestSystemLoadIsSet = false;
        private static volatile boolean qpsIsSet = false;
        private static volatile boolean maxRtIsSet = false;
        private static volatile boolean maxThreadIsSet = false;
    
        private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);
    
        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));
    
    
    • highestSystemLoad(系统负载)、qps、maxRt、maxThread是定义的审计指标值,默认值设置了最大值。
    • highestSystemLoadIsSet、qpsIsSet、maxRtIsSet、maxThreadIsSet用来标是否被设置过。
    • checkSystemStatus一个原子变量,规则检查标识,如果规则被更新了,则会设置为true。
    • statusListener:系统状态的监听器,通过线程任务启动,如果系统负载大于设置的负载,则会记录日志信息。
    • listener:实际上是一个观察者,用来监听规则是否变化,若变化则进行更新。
    • currentProperty:实际上是具体的主题DynamicSentinelProperty(这里用的是观察者模式的思想解释的😂)。
    • scheduler:定义了是定时任务线程池,线程池中只有一个线程,这个线程就是用来处理statusListener的。

    在SystemRuleManager初次加载时,会执行static静态快了代码:

     static {
            checkSystemStatus.set(false);
            statusListener = new SystemStatusListener();
            scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
            currentProperty.addListener(listener);
        }
    
    • 设置checkSystemStatus为false。
    • 创建了一个SystemStatusListener,并把statusListener添加到线程池中。
    • currentProperty中增加一个观察者listener。

    SystemRuleManager是System规则检查的核心代码,下面继续看完代码,再分析系统规则如何检测的。

     /**
         * Listen to the {@link SentinelProperty} for {@link SystemRule}s. The property is the source
         * of {@link SystemRule}s. System rules can also be set by {@link #loadRules(List)} directly.
         *
         * @param property the property to listen.
         */
        public static void register2Property(SentinelProperty<List<SystemRule>> property) {
            synchronized (listener) {
                currentProperty.removeListener(listener);
                property.addListener(listener);
                currentProperty = property;
            }
        }
    
        /**
         * Load {@link SystemRule}s, former rules will be replaced.
         *
         * @param rules new rules to load.
         */
        public static void loadRules(List<SystemRule> rules) {
            currentProperty.updateValue(rules);
        }
    
    
        public static List<SystemRule> getRules() {
    
            List<SystemRule> result = new ArrayList<SystemRule>();
            if (!checkSystemStatus.get()) {
                return result;
            }
    
            if (highestSystemLoadIsSet) {
                SystemRule loadRule = new SystemRule();
                loadRule.setHighestSystemLoad(highestSystemLoad);
                result.add(loadRule);
            }
    
            if (maxRtIsSet) {
                SystemRule rtRule = new SystemRule();
                rtRule.setAvgRt(maxRt);
                result.add(rtRule);
            }
    
            if (maxThreadIsSet) {
                SystemRule threadRule = new SystemRule();
                threadRule.setMaxThread(maxThread);
                result.add(threadRule);
            }
    
            if (qpsIsSet) {
                SystemRule qpsRule = new SystemRule();
                qpsRule.setQps(qps);
                result.add(qpsRule);
            }
    
            return result;
        }
    
    • register2Property方法:该方法其实就是把listener观察者注册到一个新的主题上,这里会在动态数据源的时候用到,比如把系统的规则写入到redis,zookeeper中等。
    • loadRules方法:通过currentProperty.updateValue方法更新系统的规则设置。
    • getRules:获取系统的规则,这里只要当规则状态checkSystemStatus是ture且设置过的系统的规则后才会获取到值。

    接收到系统动态规则后,通过SystemPropertyListener的configUpdate更新规则,下面看下System的观察者代码是怎么定义的。

    2.2.1 SystemPropertyListener

    SystemPropertyListener是SystemRuleManager的静态内部类。

    static class SystemPropertyListener extends SimplePropertyListener<List<SystemRule>> {
    
            @Override
            public void configUpdate(List<SystemRule> rules) {
                restoreSetting();
                // systemRules = rules;
                if (rules != null && rules.size() >= 1) {
                    for (SystemRule rule : rules) {
                        loadSystemConf(rule);
                    }
                } else {
                    checkSystemStatus.set(false);
                }
    
    
                RecordLog.info(String.format("[SystemRuleManager] Current system check status: %s, highestSystemLoad: "
                    + highestSystemLoad + ", " + "maxRt: %d, maxThread: %d, maxQps: " + qps, checkSystemStatus.get(), maxRt, maxThread));
            }
    
            protected void restoreSetting() {
                checkSystemStatus.set(false);
    
                // should restore changes
                highestSystemLoad = Double.MAX_VALUE;
                maxRt = Long.MAX_VALUE;
                maxThread = Long.MAX_VALUE;
                qps = Double.MAX_VALUE;
    
                highestSystemLoadIsSet = false;
                maxRtIsSet = false;
                maxThreadIsSet = false;
                qpsIsSet = false;
            }
    
        }
    

    SystemPropertyListener继承SimplePropertyListener方法,SimplePropertyListener又实现的PropertyListener的configLoad方法。
    PropertyListener是一个抽象观察接口,定义了configUpdate和configLoad方法。
    configUpdate方法就是具体的更新方法。

    所以SystemPropertyListener只要实现configUpdate方法即可。在configUpdate方法中:

    • restoreSetting方法:会重置前面介绍的9个变量值,方便规则数据更新。
    • loadSystemConf方法:设置规则,看下面代码
      public static void loadSystemConf(SystemRule rule) {
            boolean checkStatus = false;
            // Check if it's valid.
    
            if (rule.getHighestSystemLoad() >= 0) {
                highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());
                highestSystemLoadIsSet = 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);
    
        }
    

    可以看到该方法就是设置highestSystemLoad、maxRt、maxThread、qps;并且设置对应的设置标志位true。

    上述代码讲了这么多其实就是为了说明系统规则判定时,这些指标的规则是如何设置的,有了这些规则值就可以通过checkSystem方法来进行系统保护了。

     public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
            // Ensure the checking switch is on.
            if (!checkSystemStatus.get()) {
                return;
            }
    
            // for inbound traffic only
            if (resourceWrapper.getType() != EntryType.IN) {
                return;
            }
    
            // total qps
            double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
            if (currentQps > qps) {
                throw new SystemBlockException(resourceWrapper.getName(), "qps");
            }
    
            // total thread
            int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
            if (currentThread > maxThread) {
                throw new SystemBlockException(resourceWrapper.getName(), "thread");
            }
    
            double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
            if (rt > maxRt) {
                throw new SystemBlockException(resourceWrapper.getName(), "rt");
            }
    
            // BBR algorithm.
            if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
                if (currentThread > 1 &&
                    currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
                    throw new SystemBlockException(resourceWrapper.getName(), "load");
                }
            }
    
        }
    
    • checkSystemStatus,如果该状态是false是,说明规则没有设置过,直接返回。
    • 如果resourceWrapper的type不是IN的,说明资源保护不是入境调用,直接返回。
    • 拿到全局节点ENTRY_NODE,并拿到请求成功数值,线程数,平均rt的值。
    • 依次比较qps,thread,rt,load;若这些有大于系统规则的设置值,则抛出SystemBlockException异常。
    • load比较需要先获取当前系统的负载,并通过BBR算法比较系统负载是否超标。有关BBR算法可以参考网上文章。

    三、我的总结

    1、SystemSlot插槽是整个插槽链规则校验的第一个,用于系统规则设置的校验。
    2、系统规则的设置通过loadRules方法直接设置,但是通常生产环境使用时,会有一个动态数据源的接入。
    3、系统设置规则的变更用到观察者模式的思想。
    4、系统负载检测用到了BBR的算法。
    5、系统rt,qps,thread的统计数据保存在全局节点ENTRY_NODE中。


    以上内容,若有不当之处,请指正

    相关文章

      网友评论

        本文标题:Sentinel之Slots插槽源码分析系统规则(三)

        本文链接:https://www.haomeiwen.com/subject/mvmvlqtx.html