• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            寫了一個分布式名字服務JCM

            之前在公司里維護了一個名字服務,這個名字服務日常管理了近4000臺機器,有4000個左右的客戶端連接上來獲取機器信息,由于其基本是一個單點服務,所以某些模塊接近瓶頸。后來倒是有重構計劃,詳細設計做了,代碼都寫了一部分,結果由于某些原因重構就被終止了。

            JCM是我業余時間用Java重寫的一個版本,功能上目前只實現了基礎功能。由于它是個完全分布式的架構,所以理論上可以橫向擴展,大大增強系統的服務能力。

            名字服務

            在分布式系統中,某個服務為了提升整體服務能力,通常部署了很多實例。這里我把這些提供相同服務的實例統稱為集群(cluster),每個實例稱為一個節點(Node)。一個應用可能會使用很多cluster,每次訪問一個cluster時,就通過名字服務獲取該cluster下一個可用的node。那么,名字服務至少需要包含的功能:

            • 根據cluster名字獲取可用的node
            • 對管理的所有cluster下的所有node進行健康度的檢測,以保證始終返回可用的node

            有些名字服務僅對node管理,不參與應用與node間的通信,而有些則可能作為應用與node間的通信轉發器。雖然名字服務功能簡單,但是要做一個分布式的名字服務還是比較復雜的,因為數據一旦分布式了,就會存在同步、一致性問題的考慮等。

            What’s JCM

            JCM圍繞前面說的名字服務基礎功能實現。包含的功能:

            • 管理cluster到node的映射
            • 分布式架構,可水平擴展以實現管理10,000個node的能力,足以管理一般公司的后臺服務集群
            • 對每個node進行健康檢查,健康檢查可基于HTTP協議層的檢測或TCP連接檢測
            • 持久化cluster/node數據,通過zookeeper保證數據一致性
            • 提供JSON HTTP API管理cluster/node數據,后續可提供Web管理系統
            • 以庫的形式提供與server的交互,庫本身提供各種負載均衡策略,保證對一個cluster下node的訪問達到負載均衡

            項目地址git jcm

            JCM主要包含兩部分:

            • jcm.server,JCM名字服務,需要連接zookeeper以持久化數據
            • jcm.subscriber,客戶端庫,負責與jcm.server交互,提供包裝了負載均衡的API給應用使用

            架構

            基于JCM的系統整體架構如下:

            simple-arch.jpg

            cluster本身是不需要依賴JCM的,要通過JCM使用這些cluster,只需要通過JCM HTTP API注冊這些cluster到jcm.server上。要通過jcm.server使用這些cluster,則是通過jcm.subscriber來完成。

            使用

            可參考git READMe.md

            需要jre1.7+

            1. 啟動zookeeper
            2. 下載jcm.server git jcm.server-0.1.0.jar
            3. jcm.server-0.1.0.jar目錄下建立config/application.properties文件進行配置,參考config/application.properties
            4. 啟動jcm.server

               java -jar jcm.server-0.1.0.jar
              
            5. 注冊需要管理的集群,參考cluster描述:doc/cluster_sample.json,通過HTTP API注冊:

               curl -i -X POST http://10.181.97.106:8080/c -H "Content-Type:application/json" --data-binary @./doc/cluster_sample.json
              

            部署好了jcm.server,并注冊了cluster后,就可以通過jcm.subscriber使用:

            // 傳入需要使用的集群名hello9/hello,以及傳入jcm.server地址,可以多個:127.0.0.1:8080
            Subscriber subscriber = new Subscriber( Arrays.asList("127.0.0.1:8080"), Arrays.asList("hello9", "hello"));
            // 使用輪詢負載均衡策略
            RRAllocator rr = new RRAllocator();
            subscriber.addListener(rr);
            subscriber.startup();
            for (int i = 0; i < 2; ++i) {
              // rr.alloc 根據cluster名字獲取可用的node
              System.out.println(rr.alloc("hello9", ProtoType.HTTP));
            }
            subscriber.shutdown();

            JCM實現

            JCM目前的實現比較簡單,參考模塊圖:

            impl-module

            • model,即cluster/node這些數據結構的描述,同時被jcm.server和jcm.subscriber依賴
            • storage,持久化數據到zookeeper,同時包含jcm.server實例之間的數據同步
            • health check,健康檢查模塊,對各個node進行健康檢查

            以上模塊都不依賴Spring,基于以上模塊又有:

            • http api,使用spring-mvc,包裝了一些JSON HTTP API
            • Application,基于spring-boot,將各個基礎模塊組裝起來,提供standalone的模式啟動,不用部署到tomcat之類的servlet容器中

            jcm.subscriber的實現更簡單,主要是負責與jcm.server進行通信,以更新自己當前的model層數據,同時提供各種負載均衡策略接口:

            • subscriber,與jcm.server通信,定期增量拉取數據
            • node allocator,通過listener方式從subscriber中獲取數據,同時實現各種負載均衡策略,對外統一提供alloc node的接口

            接下來看看關鍵功能的實現

            數據同步

            既然jcm.server是分布式的,每一個jcm.server instance(實例)都是支持數據讀和寫的,那么當jcm.server管理著一堆cluster上萬個node時,每一個instance是如何進行數據同步的?jcm.server中的數據主要有兩類:

            • cluster本身的數據,包括cluster/node的描述,例如cluster name、node IP、及其他附屬數據
            • node健康檢查的數據

            對于cluster數據,因為cluster對node的管理是一個兩層的樹狀結構,而對cluster有增刪node的操作,所以并不能在每一個instance上都提供真正的數據寫入,這樣會導致數據丟失。假設同一時刻在instance A和instance B上同時對cluster c1添加節點N1和N2,那么instance A寫入c1(N1),而instance B還沒等到數據同步就寫入c1(N2),那么c1(N1)就被覆蓋為c1(N2),從而導致添加的節點N1丟失。

            所以,jcm.server instance是分為leaderfollower的,真正的寫入操作只有leader進行,follower收到寫操作請求時轉發給leader。leader寫數據優先更新內存中的數據再寫入zookeeper,內存中的數據更新當然是需要加鎖互斥的,從而保證數據的正確性。

            write-watch.jpg

            leader和follower是如何確定角色的?這個很簡單,標準的利用zookeeper來進行主從選舉的實現

            jcm.server instance數據間的同步是基于zookeeper watch機制的。這個可以算做是一個JCM的一個瓶頸,每一個instance都會作為一個watch,使得實際上jcm.server并不能無限水平擴展,擴展到一定程度后,watch的效率就可能不足以滿足性能了,參考zookeeper節點數與watch的性能測試 (那個時候我就在考慮對我們系統的重構了) 。

            jcm.server中對node健康檢查的數據采用同樣的同步機制,但node健康檢查數據是每一個instance都會寫入的,下面看看jcm.server是如何通過分布式架構來分擔壓力的。

            健康檢查

            jcm.server的另一個主要功能的是對node的健康檢查,jcm.server集群可以管理幾萬的node,既然已經是分布式了,那么顯然是要把node均分到多個instance的。這里我是以cluster來分配的,方法就是簡單的使用一致性哈希。通過一致性哈希,決定一個cluster是否屬于某個instance負責。每個instance都有一個server spec,也就是該instance對外提供服務的地址(IP+port),這樣在任何一個instance上,它看到的所有instance server spec都是相同的,從而保證在每一個instance上計算cluster的分配得到的結果都是一致的。

            健康檢查按cluster劃分,可以簡化數據的寫沖突問題,在正常情況下,每個instance寫入的健康檢查結果都是不同的。

            health-check.jpg

            健康檢查一般以1秒的頻率進行,jcm.server做了優化,當檢查結果和上一次一樣時,并不寫入zookeeper。寫入的數據包含了node的完整key (IP+Port+Spec),這樣可以簡化很多地方的數據同步問題,但會增加寫入數據的大小,寫入數據的大小是會影響zookeeper的性能的,所以這里簡單地對數據進行了壓縮。

            健康檢查是可以支持多種檢查實現的,目前只實現了HTTP協議層的檢查。健康檢查自身是單個線程,在該線程中基于異步HTTP庫,發起異步請求,實際的請求在其他線程中發出。

            jcm.subscriber通信

            jcm.subscriber與jcm.server通信,主要是為了獲取最新的cluster數據。subscriber初始化時會拿到一個jcm.server instance的地址列表,訪問時使用輪詢策略以平衡jcm.server在處理請求時的負載。subscriber每秒都會請求一次數據,請求中描述了本次請求想獲取哪些cluster數據,同時攜帶一個cluster的version。每次cluster在server變更時,version就變更(時間戳)。server回復請求時,如果version已最新,只需要回復node的狀態。

            subscriber可以拿到所有狀態的node,后面可以考慮只拿正常狀態的node,進一步減少數據大小。

            壓力測試

            目前只對健康檢查部分做了壓測,詳細參考test/benchmark.md。在A7服務器上測試,發現寫zookeeper及zookeeper的watch足以滿足要求,jcm.server發起的HTTP請求是主要的性能熱點,單jcm.server instance大概可以承載20000個node的健康監測。

            網絡帶寬:

            1
            2
            3
            4
            5
            
            Time              ---------------------traffic--------------------
            Time               bytin  bytout   pktin  pktout  pkterr  pktdrp
            01/07/15-21:30:48   3.2M    4.1M   33.5K   34.4K    0.00    0.00
            01/07/15-21:30:50   3.3M    4.2M   33.7K   35.9K    0.00    0.00
            01/07/15-21:30:52   2.8M    4.1M   32.6K   41.6K    0.00    0.00

            CPU,通過jstack查看主要的CPU消耗在HTTP庫實現層,以及健康檢查線程:

            1
            2
            3
            4
            
              PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
            13301 admin     20   0 13.1g 1.1g  12m R 76.6  2.3   2:40.74 java         httpchecker
            13300 admin     20   0 13.1g 1.1g  12m S 72.9  2.3   0:48.31 java
            13275 admin     20   0 13.1g 1.1g  12m S 20.1  2.3   0:18.49 java

            代碼中增加了些狀態監控:

            1
            
            checker HttpChecker stat count 20 avg check cost(ms) 542.05, avg flush cost(ms) 41.35

            表示平均每次檢查耗時542毫秒,寫數據因為開啟了cache沒有參考價值。

            雖然還可以從我自己的代碼中做不少優化,但既然單機可以承載20000個節點的檢測,一般的應用遠遠足夠了。

            總結

            名字服務在分布式系統中雖然是基礎服務,但往往承擔了非常重要的角色,數據同步出現錯誤、節點狀態出現瞬時的錯誤,都可能對整套系統造成較大影響,業務上出現較大故障。所以名字服務的健壯性、可用性非常重要。實現中需要考慮很多異常情況,包括網絡不穩定、應用層的錯誤等。為了提高足夠的可用性,一般還會加多層的數據cache,例如subscriber端的本地cache,server端的本地cache,以保證在任何情況下都不會影響應用層的服務。

            posted on 2015-07-04 17:50 Kevin Lynx 閱讀(1744) 評論(0)  編輯 收藏 引用

            久久婷婷五月综合成人D啪 | 久久久99精品成人片中文字幕| 久久w5ww成w人免费| 国产精品久久国产精麻豆99网站| 久久99国产综合精品免费| 亚洲狠狠综合久久| 亚洲欧美日韩精品久久亚洲区| 伊人久久大香线焦AV综合影院 | 亚洲中文精品久久久久久不卡| 久久人人爽人人爽人人AV东京热 | 国产亚洲精午夜久久久久久| 午夜精品久久久久久影视riav| 久久久久亚洲av无码专区| 精品熟女少妇aⅴ免费久久| 久久精品aⅴ无码中文字字幕不卡 久久精品成人欧美大片 | 浪潮AV色综合久久天堂| A级毛片无码久久精品免费| 怡红院日本一道日本久久| 18禁黄久久久AAA片| 色综合久久中文综合网| 亚洲成色www久久网站夜月| 久久久精品人妻无码专区不卡| 久久99精品久久久久久久不卡| 2020国产成人久久精品| 久久国产午夜精品一区二区三区| 久久国产色AV免费看| 久久久久久久免费视频| 欧美久久一区二区三区| 97久久精品人人做人人爽| 久久精品国产亚洲AV电影| 亚洲人成无码久久电影网站| AAA级久久久精品无码区| 欧美精品一区二区精品久久| 久久久亚洲欧洲日产国码二区| 亚洲欧美一区二区三区久久| 久久夜色撩人精品国产| 国内精品久久久久影院网站| 999久久久国产精品| 久久一区二区免费播放| 久久天天躁狠狠躁夜夜2020 | 久久国产精品免费|