soul網(wǎng)關(guān)學(xué)習(xí)15-插件實(shí)現(xiàn)1-Divide1-后端節(jié)點(diǎn)探活

前面幾篇我們重點(diǎn)去挖掘了網(wǎng)關(guān)配置數(shù)據(jù)的同步,接下來我們會去分析soul網(wǎng)關(guān)的插件體系带兜,就開始吧。

前言

我們知道網(wǎng)關(guān)最核心的能力是進(jìn)行http請求的轉(zhuǎn)發(fā)斩熊。那在我們的soul網(wǎng)關(guān)中又是如何實(shí)現(xiàn)這一功能的里逆?這里的實(shí)現(xiàn)就是我們今天要分析的主題divide插件进胯。

分析

  1. 先從configuration入手,找到divide插件的配置類DividePluginConfiguration原押。
    DividePluginConfiguration
  2. 從上圖中可以看到配置類創(chuàng)建了幾個關(guān)鍵的bean胁镐。我們先來分析DividePlugin,核心邏輯doExecute诸衔。
    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        // context模式
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        // 獲取分流規(guī)則handle
        final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
        // 根據(jù)當(dāng)前選擇器獲取到后臺節(jié)點(diǎn)list
        // TODO 需分析后臺節(jié)點(diǎn)獲取的實(shí)現(xiàn)盯漂,會涉及到后臺節(jié)點(diǎn)的探活,增加活剔除節(jié)點(diǎn)
        final List<DivideUpstream> upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
        // 如果獲取到的后臺節(jié)點(diǎn)為空笨农,則直接返回
        if (CollectionUtils.isEmpty(upstreamList)) {
            log.error("divide upstream configuration error: {}", rule.toString());
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // 獲取ip
        final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        // 根據(jù)遠(yuǎn)程客戶端ip獲取后臺節(jié)點(diǎn)就缆,經(jīng)過負(fù)載均衡策略
        // TODO 需分析負(fù)載均衡的實(shí)現(xiàn)
        DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
        // 經(jīng)過負(fù)載均衡策略后,未選擇到節(jié)點(diǎn)則返回錯誤
        if (Objects.isNull(divideUpstream)) {
            log.error("divide has no upstream");
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // set the http url
        // 根據(jù)后臺節(jié)點(diǎn)對象構(gòu)建服務(wù)地址谒亦,并將其放于交換器竭宰,傳遞下去,進(jìn)行轉(zhuǎn)發(fā)
        String domain = buildDomain(divideUpstream);
        String realURL = buildRealURL(domain, soulContext, exchange);
        // 調(diào)用的幾個關(guān)鍵參數(shù):服務(wù)地址 請求參數(shù) 超時 重試次數(shù)
        exchange.getAttributes().put(Constants.HTTP_URL, realURL);
        // set the http timeout
        exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
        exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
        return chain.execute(exchange);
    }
DividePlugin.doExecute
  1. 從上圖中我們可以繼續(xù)跟蹤兩個子邏輯【后端節(jié)點(diǎn)列表的獲取】以及【負(fù)載均衡算法】

后端節(jié)點(diǎn)列表的獲取

soul-bootstrap后端節(jié)點(diǎn)探活

  1. UpstreamCacheManager實(shí)現(xiàn)份招,其中后端節(jié)點(diǎn)探活的結(jié)構(gòu)圖如下
    UpstreamCacheManager.check
  2. 我們來分解一下這個圖
    • 存放后端節(jié)點(diǎn)數(shù)據(jù)有兩個 map切揭,UPSTREAM_MAPUPSTREAM_MAP_TEMP。至于為什么會有兩個map锁摔,主要是前者會存儲所有的后端節(jié)點(diǎn)廓旬,由soul-admin那邊同步過來,不管其是否存活谐腰;而后者則只存儲探活成功的節(jié)點(diǎn)孕豹,為網(wǎng)關(guān)負(fù)載的有效節(jié)點(diǎn)。
    • 還有個scheduler十气,該scheduler開啟了一個線程scheduled-upstream-task執(zhí)行調(diào)度任務(wù)scheduled励背,默認(rèn)探活調(diào)度的間隔為30s,建議設(shè)置為1s桦踊,需手設(shè)置
    • scheduled邏輯是遍歷所有后端節(jié)點(diǎn)UPSTREAM_MAP椅野,通過挨個探活check终畅,完成有效后端節(jié)點(diǎn)UPSTREAM_MAP_TEMP的替換與剔除
    • 這里探活check的實(shí)現(xiàn)方式:后端節(jié)點(diǎn)upstream服務(wù)地址url是否為ip籍胯,為ip則用ip+port建立socket連接進(jìn)行探測;否則直接判斷節(jié)點(diǎn)host是否可達(dá)离福;采用socket方式探活時沒有顯式的超時設(shè)置杖狼,而host是否可達(dá)則會1s超時
  3. 以上只是分析了upstream的探活實(shí)現(xiàn),當(dāng)然還有所有后端節(jié)點(diǎn)UPSTREAM_MAP數(shù)據(jù)的初始化與變更
  4. 這個邏輯就比較簡單妖爷,通過公共的數(shù)據(jù)變更機(jī)制handler蝶涩,在選擇器selector配置發(fā)生變化的時候理朋,進(jìn)行相應(yīng)選擇器后端節(jié)點(diǎn)列表的添加add與移除remove

補(bǔ)充soul-admin端的后端節(jié)點(diǎn)探活

  1. 關(guān)鍵類UpstreamCheckService,我們看到這個類在實(shí)例化之后的時候就會去執(zhí)行后端節(jié)點(diǎn)探活的邏輯
    @PostConstruct
    public void setup() {
        // 從數(shù)據(jù)庫中獲取到所有的后端節(jié)點(diǎn)數(shù)據(jù)绿聘,初始邏輯
        PluginDO pluginDO = pluginMapper.selectByName(PluginEnum.DIVIDE.getName());
        if (pluginDO != null) {
            List<SelectorDO> selectorDOList = selectorMapper.findByPluginId(pluginDO.getId());
            for (SelectorDO selectorDO : selectorDOList) {
                List<DivideUpstream> divideUpstreams = GsonUtils.getInstance().fromList(selectorDO.getHandle(), DivideUpstream.class);
                if (CollectionUtils.isNotEmpty(divideUpstreams)) {
                    UPSTREAM_MAP.put(selectorDO.getName(), divideUpstreams);
                }
            }
        }
        // 如果探活開啟嗽上,則會執(zhí)行探活,可配置熄攘,默認(rèn)是開啟的
        if (check) {
            // 開啟調(diào)度線程池默認(rèn)每10s執(zhí)行一次check
            new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
                    .scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
        }
    }
  1. 從上得知啟動過程會開啟調(diào)度線程池兽愤,默認(rèn)每10s執(zhí)行后端探活的邏輯scheduled方法,探活線程的個數(shù)為cpu的核心數(shù)
  2. 這里對后端節(jié)點(diǎn)的探活邏輯與上述soul-bootstrap端類似挪圾,只是在得到存活節(jié)點(diǎn)的list之后浅萧,會判斷是否存在后端節(jié)點(diǎn)數(shù)目的變化(這里的探活不會增加新節(jié)點(diǎn),只有可能剔除一些不存活的節(jié)點(diǎn)哲思,所以只要存在存活節(jié)點(diǎn)數(shù)與所有節(jié)點(diǎn)不一致的情況洼畅,則就發(fā)生了變更
  3. 如果存在變化,則會執(zhí)行updateSelectorHandler的邏輯棚赔,該邏輯主要會做兩件事情:
    • 將存活節(jié)點(diǎn)list更新到soul-admin數(shù)據(jù)庫帝簇,保存起來
    • 同時發(fā)布selector配置變更的事件給到soul-bootstrapsoul-bootstrap端就會更新其后端節(jié)點(diǎn)的數(shù)據(jù)靠益,完成探活節(jié)點(diǎn)的同步

負(fù)載均衡算法

下篇講解己儒,To be contined...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捆毫,隨后出現(xiàn)的幾起案子闪湾,更是在濱河造成了極大的恐慌,老刑警劉巖绩卤,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件途样,死亡現(xiàn)場離奇詭異,居然都是意外死亡濒憋,警方通過查閱死者的電腦和手機(jī)何暇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凛驮,“玉大人裆站,你說我怎么就攤上這事∏玻” “怎么了宏胯?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長本姥。 經(jīng)常有香客問我肩袍,道長,這世上最難降的妖魔是什么婚惫? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任氛赐,我火速辦了婚禮魂爪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艰管。我一直安慰自己滓侍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布牲芋。 她就那樣靜靜地躺著粗井,像睡著了一般。 火紅的嫁衣襯著肌膚如雪街图。 梳的紋絲不亂的頭發(fā)上浇衬,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音餐济,去河邊找鬼耘擂。 笑死,一個胖子當(dāng)著我的面吹牛絮姆,可吹牛的內(nèi)容都是我干的醉冤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼篙悯,長吁一口氣:“原來是場噩夢啊……” “哼蚁阳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸽照,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤螺捐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矮燎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體定血,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年诞外,在試婚紗的時候發(fā)現(xiàn)自己被綠了澜沟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡峡谊,死狀恐怖茫虽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情既们,我是刑警寧澤濒析,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站贤壁,受9級特大地震影響悼枢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脾拆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一馒索、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧名船,春花似錦绰上、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至迷扇,卻和暖如春百揭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜓席。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工器一, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厨内。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓祈秕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雏胃。 傳聞我的和親對象是個殘疾皇子请毛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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