Sentinel之Slots插槽源碼分析系統(tǒng)規(guī)則(三)

一、引子

前面的介紹過插槽鏈的NodeSelectorSlot和ClusterBuilderSlot,見Sentinel之Slots插槽源碼分析(一)這篇文章灭必。
還介紹了LogSlot和StatisticSlot妹笆,見Sentinel之Slots插槽源碼分析(二)這篇文章。

接下來(lái)我們開始分析SystemSlot浮创。

二、SystemSlot

SystemSlot主要是用來(lái)系統(tǒng)規(guī)則的檢查,包括平均RT忆谓,qps,線程數(shù)踱承,系統(tǒng)負(fù)載(只是針對(duì)linux系統(tǒng))倡缠。

下面具體分析SystemSlot是如何實(shí)現(xiàn)系統(tǒng)檢查的。

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);
    }

}

可以看到調(diào)用了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(系統(tǒng)負(fù)載)、qps载荔、maxRt盾饮、maxThread是定義的審計(jì)指標(biāo)值,默認(rèn)值設(shè)置了最大值懒熙。
  • highestSystemLoadIsSet丘损、qpsIsSet、maxRtIsSet工扎、maxThreadIsSet用來(lái)標(biāo)是否被設(shè)置過徘钥。
  • checkSystemStatus一個(gè)原子變量,規(guī)則檢查標(biāo)識(shí)肢娘,如果規(guī)則被更新了呈础,則會(huì)設(shè)置為true。
  • statusListener:系統(tǒng)狀態(tài)的監(jiān)聽器橱健,通過線程任務(wù)啟動(dòng)而钞,如果系統(tǒng)負(fù)載大于設(shè)置的負(fù)載,則會(huì)記錄日志信息拘荡。
  • listener:實(shí)際上是一個(gè)觀察者笨忌,用來(lái)監(jiān)聽規(guī)則是否變化,若變化則進(jìn)行更新俱病。
  • currentProperty:實(shí)際上是具體的主題DynamicSentinelProperty(這里用的是觀察者模式的思想解釋的??)官疲。
  • scheduler:定義了是定時(shí)任務(wù)線程池,線程池中只有一個(gè)線程亮隙,這個(gè)線程就是用來(lái)處理statusListener的途凫。

在SystemRuleManager初次加載時(shí),會(huì)執(zhí)行static靜態(tài)快了代碼:

 static {
        checkSystemStatus.set(false);
        statusListener = new SystemStatusListener();
        scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
        currentProperty.addListener(listener);
    }
  • 設(shè)置checkSystemStatus為false溢吻。
  • 創(chuàng)建了一個(gè)SystemStatusListener维费,并把statusListener添加到線程池中果元。
  • currentProperty中增加一個(gè)觀察者listener。

SystemRuleManager是System規(guī)則檢查的核心代碼犀盟,下面繼續(xù)看完代碼而晒,再分析系統(tǒng)規(guī)則如何檢測(cè)的。

 /**
     * 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方法:該方法其實(shí)就是把listener觀察者注冊(cè)到一個(gè)新的主題上阅畴,這里會(huì)在動(dòng)態(tài)數(shù)據(jù)源的時(shí)候用到倡怎,比如把系統(tǒng)的規(guī)則寫入到redis,zookeeper中等贱枣。
  • loadRules方法:通過currentProperty.updateValue方法更新系統(tǒng)的規(guī)則設(shè)置监署。
  • getRules:獲取系統(tǒng)的規(guī)則,這里只要當(dāng)規(guī)則狀態(tài)checkSystemStatus是ture且設(shè)置過的系統(tǒng)的規(guī)則后才會(huì)獲取到值纽哥。

接收到系統(tǒng)動(dòng)態(tài)規(guī)則后钠乏,通過SystemPropertyListener的configUpdate更新規(guī)則,下面看下System的觀察者代碼是怎么定義的春塌。

2.2.1 SystemPropertyListener

SystemPropertyListener是SystemRuleManager的靜態(tài)內(nèi)部類晓避。

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又實(shí)現(xiàn)的PropertyListener的configLoad方法只壳。
PropertyListener是一個(gè)抽象觀察接口够滑,定義了configUpdate和configLoad方法。
configUpdate方法就是具體的更新方法吕世。

所以SystemPropertyListener只要實(shí)現(xiàn)configUpdate方法即可彰触。在configUpdate方法中:

  • restoreSetting方法:會(huì)重置前面介紹的9個(gè)變量值,方便規(guī)則數(shù)據(jù)更新命辖。
  • loadSystemConf方法:設(shè)置規(guī)則,看下面代碼
  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);

    }

可以看到該方法就是設(shè)置highestSystemLoad况毅、maxRt、maxThread尔艇、qps尔许;并且設(shè)置對(duì)應(yīng)的設(shè)置標(biāo)志位true。

上述代碼講了這么多其實(shí)就是為了說明系統(tǒng)規(guī)則判定時(shí)终娃,這些指標(biāo)的規(guī)則是如何設(shè)置的味廊,有了這些規(guī)則值就可以通過checkSystem方法來(lái)進(jìn)行系統(tǒng)保護(hù)了。

 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,如果該狀態(tài)是false是棠耕,說明規(guī)則沒有設(shè)置過余佛,直接返回。
  • 如果resourceWrapper的type不是IN的窍荧,說明資源保護(hù)不是入境調(diào)用辉巡,直接返回。
  • 拿到全局節(jié)點(diǎn)ENTRY_NODE蕊退,并拿到請(qǐng)求成功數(shù)值郊楣,線程數(shù)憔恳,平均rt的值。
  • 依次比較qps净蚤,thread钥组,rt,load今瀑;若這些有大于系統(tǒng)規(guī)則的設(shè)置值程梦,則拋出SystemBlockException異常。
  • load比較需要先獲取當(dāng)前系統(tǒng)的負(fù)載放椰,并通過BBR算法比較系統(tǒng)負(fù)載是否超標(biāo)。有關(guān)BBR算法可以參考網(wǎng)上文章愉粤。

三砾医、我的總結(jié)

1、SystemSlot插槽是整個(gè)插槽鏈規(guī)則校驗(yàn)的第一個(gè)衣厘,用于系統(tǒng)規(guī)則設(shè)置的校驗(yàn)如蚜。
2、系統(tǒng)規(guī)則的設(shè)置通過loadRules方法直接設(shè)置影暴,但是通常生產(chǎn)環(huán)境使用時(shí)错邦,會(huì)有一個(gè)動(dòng)態(tài)數(shù)據(jù)源的接入。
3型宙、系統(tǒng)設(shè)置規(guī)則的變更用到觀察者模式的思想撬呢。
4、系統(tǒng)負(fù)載檢測(cè)用到了BBR的算法妆兑。
5魂拦、系統(tǒng)rt,qps搁嗓,thread的統(tǒng)計(jì)數(shù)據(jù)保存在全局節(jié)點(diǎn)ENTRY_NODE中芯勘。


以上內(nèi)容,若有不當(dāng)之處腺逛,請(qǐng)指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末荷愕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棍矛,更是在濱河造成了極大的恐慌安疗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件够委,死亡現(xiàn)場(chǎng)離奇詭異茂契,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)慨绳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門掉冶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)真竖,“玉大人,你說我怎么就攤上這事厌小』止玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵璧亚,是天一觀的道長(zhǎng)讨韭。 經(jīng)常有香客問我,道長(zhǎng)癣蟋,這世上最難降的妖魔是什么透硝? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮疯搅,結(jié)果婚禮上濒生,老公的妹妹穿的比我還像新娘。我一直安慰自己幔欧,他們只是感情好罪治,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著礁蔗,像睡著了一般觉义。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浴井,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天晒骇,我揣著相機(jī)與錄音,去河邊找鬼磺浙。 笑死厉碟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屠缭。 我是一名探鬼主播箍鼓,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呵曹!你這毒婦竟也來(lái)了款咖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奄喂,失蹤者是張志新(化名)和其女友劉穎铐殃,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跨新,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡富腊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了域帐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赘被。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡是整,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出民假,到底是詐尸還是另有隱情浮入,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布羊异,位于F島的核電站事秀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏野舶。R本人自食惡果不足惜易迹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望平道。 院中可真熱鬧睹欲,春花似錦、人聲如沸巢掺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)陆淀。三九已至,卻和暖如春先嬉,著一層夾襖步出監(jiān)牢的瞬間轧苫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工疫蔓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留含懊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓衅胀,卻偏偏與公主長(zhǎng)得像岔乔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滚躯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一雏门、概述 前面介紹過Sentinel核心框架就是通過插槽鏈一層層的調(diào)用,每個(gè)插槽的功能如下: NodeSelect...
    橘子_好多灰閱讀 1,529評(píng)論 0 4
  • 一掸掏、概述 上篇文章介紹過了NodeSelectorSlot和ClusterBuilderSlot兩種插槽茁影,接下來(lái)我...
    橘子_好多灰閱讀 1,404評(píng)論 1 1
  • Java繼承關(guān)系初始化順序 父類的靜態(tài)變量-->父類的靜態(tài)代碼塊-->子類的靜態(tài)變量-->子類的靜態(tài)代碼快-->父...
    第六象限閱讀 2,157評(píng)論 0 9
  • 睡覺吧募闲,貓咪, 讓微風(fēng)撫摸著你的臉龐入睡吧愿待。 睡覺吧浩螺,貓咪靴患, 讓陽(yáng)光伴隨著你入眠。 睡覺吧年扩,貓咪蚁廓, 讓小鳥的歡唱使...
    周一秩禾閱讀 227評(píng)論 0 3
  • 媽媽肚子里的小寶貝 翻開自己在李躍兒芭學(xué)園群里分享的《懷孕六個(gè)月,我闖進(jìn)了李躍兒芭學(xué)園的殿堂》的文章厨幻,莫名的還能回...
    楊福榮閱讀 470評(píng)論 2 2