成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

分布式(一) 搞定服務(wù)注冊(cè)與發(fā)現(xiàn)

Integ / 3251人閱讀

摘要:但同樣又會(huì)出現(xiàn)新的問題,如果服務(wù)提供者的節(jié)點(diǎn)新增或者刪除消費(fèi)者這邊根本就不知道情況。通常來(lái)說(shuō)消費(fèi)者是需要知道服務(wù)提供者的網(wǎng)絡(luò)地址才能發(fā)起遠(yuǎn)程調(diào)用,這塊內(nèi)容和我上面的需求其實(shí)非常類似。

背景

最近在做分布式相關(guān)的工作,由于人手不夠只能我一個(gè)人來(lái)懟;看著這段時(shí)間的加班表想想就是夠慘的。

不過(guò)其中也有遇到的不少有意思的事情今后再拿來(lái)分享,今天重點(diǎn)來(lái)討論服務(wù)的注冊(cè)與發(fā)現(xiàn)

分布式帶來(lái)的問題

我的業(yè)務(wù)比較簡(jiǎn)單,只是需要知道現(xiàn)在有哪些服務(wù)實(shí)例可供使用就可以了(并不是做遠(yuǎn)程調(diào)用,只需要拿到信息即可)。

要實(shí)現(xiàn)這一功能最簡(jiǎn)單的方式可以在應(yīng)用中配置所有的服務(wù)節(jié)點(diǎn),這樣每次在使用時(shí)只需要通過(guò)某種算法從配置列表中選擇一個(gè)就可以了。

但這樣會(huì)有一個(gè)非常嚴(yán)重的問題:

由于應(yīng)用需要根據(jù)應(yīng)用負(fù)載情況來(lái)靈活的調(diào)整服務(wù)節(jié)點(diǎn)的數(shù)量,這樣我的配置就不能寫死。

不然就會(huì)出現(xiàn)要么新增的節(jié)點(diǎn)沒有訪問或者是已經(jīng) down 掉的節(jié)點(diǎn)卻有請(qǐng)求,這樣肯定是不行的。

往往要解決這類分布式問題都需要一個(gè)公共的區(qū)域來(lái)保存這些信息,比如是否可以利用 Redis?

每個(gè)節(jié)點(diǎn)啟動(dòng)之后都向 Redis 注冊(cè)信息,關(guān)閉時(shí)也刪除數(shù)據(jù)。

其實(shí)就是存放節(jié)點(diǎn)的 ip + port,然后在需要知道服務(wù)節(jié)點(diǎn)信息時(shí)候只需要去 Redis 中獲取即可。

如下圖所示:

但這樣會(huì)導(dǎo)致每次使用時(shí)都需要頻繁的去查詢 Redis,為了避免這個(gè)問題我們可以在每次查詢之后在本地緩存一份最新的數(shù)據(jù)。這樣優(yōu)先從本地獲取確實(shí)可以提高效率。

但同樣又會(huì)出現(xiàn)新的問題,如果服務(wù)提供者的節(jié)點(diǎn)新增或者刪除消費(fèi)者這邊根本就不知道情況。

要解決這個(gè)問題最先想到的應(yīng)該就是利用定時(shí)任務(wù)定期去更新服務(wù)列表。

以上的方案肯定不完美,并且不優(yōu)雅。主要有以下幾點(diǎn):

基于定時(shí)任務(wù)會(huì)導(dǎo)致很多無(wú)效的更新。

定時(shí)任務(wù)存在周期性,沒法做到實(shí)時(shí),這樣就可能存在請(qǐng)求異常。

如果服務(wù)被強(qiáng)行 kill,沒法及時(shí)清除 Redis,這樣這個(gè)看似可用的服務(wù)將永遠(yuǎn)不可用!

所以我們需要一個(gè)更加靠譜的解決方案,這樣的場(chǎng)景其實(shí)和 Dubbo 非常類似。

用過(guò)的同學(xué)肯定對(duì)這張圖不陌生。

引用自 Dubbo 官網(wǎng)

其中有一塊非常核心的內(nèi)容(紅框出)就是服務(wù)的注冊(cè)與發(fā)現(xiàn)。

通常來(lái)說(shuō)消費(fèi)者是需要知道服務(wù)提供者的網(wǎng)絡(luò)地址(ip + port)才能發(fā)起遠(yuǎn)程調(diào)用,這塊內(nèi)容和我上面的需求其實(shí)非常類似。

而 Dubbo 則是利用 Zookeeper 來(lái)解決問題。

Zookeeper 能做什么

在具體討論怎么實(shí)現(xiàn)之前先看看 Zookeeper 的幾個(gè)重要特性。

Zookeeper 實(shí)現(xiàn)了一個(gè)類似于文件系統(tǒng)的樹狀結(jié)構(gòu):

這些節(jié)點(diǎn)被稱為 znode(名字叫什么不重要),其中每個(gè)節(jié)點(diǎn)都可以存放一定的數(shù)據(jù)。

最主要的是 znode 有四種類型:

永久節(jié)點(diǎn)(除非手動(dòng)刪除,節(jié)點(diǎn)永遠(yuǎn)存在)

永久有序節(jié)點(diǎn)(按照創(chuàng)建順序會(huì)為每個(gè)節(jié)點(diǎn)末尾帶上一個(gè)序號(hào)如:root-1

瞬時(shí)節(jié)點(diǎn)(創(chuàng)建客戶端與 Zookeeper 保持連接時(shí)節(jié)點(diǎn)存在,斷開時(shí)則刪除并會(huì)有相應(yīng)的通知)

瞬時(shí)有序節(jié)點(diǎn)(在瞬時(shí)節(jié)點(diǎn)的基礎(chǔ)上加上了順序)

考慮下上文使用 Redis 最大的一個(gè)問題是什么?

其實(shí)就是不能實(shí)時(shí)的更新服務(wù)提供者的信息。

那利用 Zookeeper 是怎么實(shí)現(xiàn)的?

主要看第三個(gè)特性:瞬時(shí)節(jié)點(diǎn)

Zookeeper 是一個(gè)典型的觀察者模式。

由于瞬時(shí)節(jié)點(diǎn)的特點(diǎn),我們的消費(fèi)者可以訂閱瞬時(shí)節(jié)點(diǎn)的父節(jié)點(diǎn)。

當(dāng)新增、刪除節(jié)點(diǎn)時(shí)所有的瞬時(shí)節(jié)點(diǎn)也會(huì)自動(dòng)更新。

更新時(shí)會(huì)給訂閱者發(fā)起通知告訴最新的節(jié)點(diǎn)信息。

這樣我們就可以實(shí)時(shí)獲取服務(wù)節(jié)點(diǎn)的信息,同時(shí)也只需要在第一次獲取列表時(shí)緩存到本地;也不需要頻繁和 Zookeeper 產(chǎn)生交互,只用等待通知更新即可。

并且不管應(yīng)用什么原因節(jié)點(diǎn) down 掉后也會(huì)在 Zookeeper 中刪除該信息。

效果演示

這樣實(shí)現(xiàn)方式就變?yōu)檫@樣。

為此我新建了一個(gè)應(yīng)用來(lái)進(jìn)行演示:

https://github.com/crossoverJie/netty-action/tree/master/netty-action-zk

就是一個(gè)簡(jiǎn)單的 SpringBoot 應(yīng)用,只是做了幾件事情。

應(yīng)用啟動(dòng)時(shí)新開一個(gè)線程用于向 Zookeeper 注冊(cè)服務(wù)。

同時(shí)監(jiān)聽一個(gè)節(jié)點(diǎn)用于更新本地服務(wù)列表。

提供一個(gè)接口用于返回一個(gè)可有的服務(wù)節(jié)點(diǎn)。

我在本地啟動(dòng)了兩個(gè)應(yīng)用分別是:127.0.0.1:8083,127.0.0.1:8084。來(lái)看看效果圖。

兩個(gè)應(yīng)用啟動(dòng)完成:

當(dāng)前 Zookeeper 的可視化樹狀結(jié)構(gòu):

當(dāng)想知道所有的服務(wù)節(jié)點(diǎn)信息時(shí):

想要獲取一個(gè)可用的服務(wù)節(jié)點(diǎn)時(shí):

這里只是采取了簡(jiǎn)單的輪詢。

當(dāng) down 掉一個(gè)節(jié)點(diǎn)時(shí):應(yīng)用會(huì)收到通知更新本地緩存。同時(shí) Zookeeper 中的節(jié)點(diǎn)會(huì)自動(dòng)刪除。

再次獲取最新節(jié)點(diǎn)時(shí):

當(dāng)節(jié)點(diǎn)恢復(fù)時(shí)自然也能獲取到最新信息。本地緩存也會(huì)及時(shí)更新。

編碼實(shí)現(xiàn)

實(shí)現(xiàn)起來(lái)倒也比較簡(jiǎn)單,主要就是 ZKClient 的 api 使用。

貼幾段比較核心的吧。

注冊(cè)
啟動(dòng)注冊(cè) Zookeeper。

主要邏輯都在這個(gè)線程中。

首先創(chuàng)建父節(jié)點(diǎn)。如上圖的 Zookeeper 節(jié)點(diǎn)所示;需要先創(chuàng)建 /route 根節(jié)點(diǎn),創(chuàng)建的時(shí)候會(huì)判斷是否已經(jīng)存在。

接著需要判斷是否需要將自己注冊(cè)到 Zookeeper 中,因?yàn)橛行┕?jié)點(diǎn)只是用于服務(wù)發(fā)現(xiàn),他自身是不需要承擔(dān)業(yè)務(wù)功能(是我自己項(xiàng)目的需求)。

將當(dāng)前應(yīng)用的所在 ip 以及端口注冊(cè)上去,同時(shí)需要監(jiān)聽根節(jié)點(diǎn) /route ,這樣才能在其他服務(wù)上下線時(shí)候獲得通知。

根據(jù)本地緩存
監(jiān)聽到服務(wù)變化
    public void subscribeEvent(String path) {
        zkClient.subscribeChildChanges(path, new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List currentChilds) throws Exception {
                logger.info("清除/更新本地緩存 parentPath=【{}】,currentChilds=【{}】", parentPath,currentChilds.toString());

                //更新所有緩存/先刪除 再新增
                serverCache.updateCache(currentChilds) ;
            }
        });


    }

可以看到這里是更新了本地緩存,該緩存采用了 Guava 提供的 Cache,感興趣的可以查看之前的源碼分析。

    /**
     * 更新所有緩存/先刪除 再新增
     *
     * @param currentChilds
     */
    public void updateCache(List currentChilds) {
        cache.invalidateAll();
        for (String currentChild : currentChilds) {
            String key = currentChild.split("-")[1];
            addCache(key);
        }
    }
客戶端負(fù)載
同時(shí)在客戶端提供了一個(gè)負(fù)載算法。

其實(shí)就是一個(gè)輪詢的實(shí)現(xiàn):

    /**
     * 選取服務(wù)器
     *
     * @return
     */
    public String selectServer() {
        List all = getAll();
        if (all.size() == 0) {
            throw new RuntimeException("路由列表為空");
        }
        Long position = index.incrementAndGet() % all.size();
        if (position < 0) {
            position = 0L;
        }

        return all.get(position.intValue());
    }

當(dāng)然這里可以擴(kuò)展出更多的如權(quán)重、隨機(jī)、LRU 等算法。

Zookeeper 其他優(yōu)勢(shì)及問題

Zookeeper 自然是一個(gè)很棒的分布式協(xié)調(diào)工具,利用它的特性還可以有其他作用。

數(shù)據(jù)變更發(fā)送通知這一特性可以實(shí)現(xiàn)統(tǒng)一配置中心,再也不需要在每個(gè)服務(wù)中多帶帶維護(hù)配置。

利用瞬時(shí)有序節(jié)點(diǎn)還可以實(shí)現(xiàn)分布式鎖。

在實(shí)現(xiàn)注冊(cè)、發(fā)現(xiàn)這一需求時(shí),Zookeeper 其實(shí)并不是最優(yōu)選。

由于 Zookeeper 在 CAP 理論中選擇了 CP(一致性、分區(qū)容錯(cuò)性),當(dāng) Zookeeper 集群有半數(shù)節(jié)點(diǎn)不可用時(shí)是不能獲取到任何數(shù)據(jù)的。

對(duì)于一致性來(lái)說(shuō)自然沒啥問題,但在注冊(cè)、發(fā)現(xiàn)的場(chǎng)景下更加推薦 Eureka,已經(jīng)在 SpringCloud 中得到驗(yàn)證。具體就不在本文討論了。

但鑒于我的使用場(chǎng)景來(lái)說(shuō) Zookeeper 已經(jīng)能夠勝任。

總結(jié)

本文所有完整代碼都托管在 GitHub。

https://github.com/crossoverJie/netty-action。

一個(gè)看似簡(jiǎn)單的注冊(cè)、發(fā)現(xiàn)功能實(shí)現(xiàn)了,但分布式應(yīng)用遠(yuǎn)遠(yuǎn)不止這些。

由于網(wǎng)絡(luò)隔離之后帶來(lái)的一系列問題還需要我們用其他方式一一完善;后續(xù)會(huì)繼續(xù)更新分布式相關(guān)內(nèi)容,感興趣的朋友不妨持續(xù)關(guān)注。

你的點(diǎn)贊與轉(zhuǎn)發(fā)是最大的支持。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76869.html

相關(guān)文章

  • 服務(wù)注冊(cè)中心注冊(cè)hashcode實(shí)現(xiàn)golang版

    摘要:背景基于負(fù)載均衡的服務(wù)調(diào)用基于負(fù)載均衡的服務(wù)相互調(diào)用指的是通過(guò)基于等負(fù)載均衡軟件來(lái)構(gòu)建一個(gè)負(fù)載均衡服務(wù),所有的服務(wù)調(diào)用都通過(guò)負(fù)載均衡器從負(fù)載均衡的這種模式下其實(shí)有兩個(gè)主要的問題一是中心化,整個(gè)系統(tǒng)都基于負(fù)載均衡器,負(fù)載均衡就相當(dāng)于整個(gè)業(yè)背景 基于負(fù)載均衡的服務(wù)調(diào)用 showImg(https://user-gold-cdn.xitu.io/2019/5/23/16ae29255c5f47b1...

    KitorinZero 評(píng)論0 收藏0
  • vue2.0開發(fā)聊天程序(六) 搞定mongodb

    摘要:為安裝文件,無(wú)需再配置環(huán)境變量。連接操作有以下包作者并未查到除此之外的包,但不代表沒有。等于是每個(gè)默認(rèn)配置的主鍵屬性,屬性名為可自己定義一個(gè)來(lái)覆蓋此屬性。需要注意的是,在新版本的文檔中,為。通過(guò)創(chuàng)建限于篇幅,本小節(jié)暫時(shí)寫到這里。 我的琴聲嗚咽,我的淚水全無(wú)。我把遠(yuǎn)方的遠(yuǎn)歸還草原?!                  ? 海子《九月》 mongodb安裝 什么是Mongodb?就是一個(gè)基...

    Dr_Noooo 評(píng)論0 收藏0
  • 二十五歲零基礎(chǔ)轉(zhuǎn)行做軟件測(cè)試怎么樣?個(gè)過(guò)來(lái)人的心路歷程送給迷茫的你

    摘要:一個(gè)軟件測(cè)試在職老人幫你詳細(xì)分析一下。在軟件測(cè)試行業(yè),前兩點(diǎn)可以結(jié)合起來(lái)說(shuō),就是大環(huán)境和前景以及人才缺口的問題。軟件測(cè)試屬于互聯(lián)網(wǎng)技術(shù)的一個(gè)分支,就是經(jīng)常被提到的行業(yè)。你零基礎(chǔ)轉(zhuǎn)行嗷,良心奉勸你不要自學(xué)。 一個(gè)軟件測(cè)試在職老人幫你詳細(xì)分析一下。先不說(shuō)軟件測(cè)試領(lǐng)域,你想轉(zhuǎn)行的話,得知道這個(gè)行...

    AlienZHOU 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<