隨著業(yè)務(wù)的發(fā)展,用戶量日益上升,單一的系統(tǒng)越來越復(fù)雜,越來越龐大,單純的提升服務(wù)器性能始終有頂天的一天,我們可以通過分布式技術(shù),例如:服務(wù)器集群,水平業(yè)務(wù)劃分,應(yīng)用分解,系統(tǒng)分流,微服務(wù)架構(gòu)等方式來解決系統(tǒng)性能問題和復(fù)雜業(yè)務(wù)問題。
第一篇:架構(gòu)演進(jìn)
在分布式體系下服務(wù)注冊與發(fā)現(xiàn)將會(huì)以核心組件而存在,也將是接下來討論的話題。
Lazy服務(wù)注冊與發(fā)現(xiàn)(一)
由于應(yīng)用的分解,微服務(wù)的引入,服務(wù)越來越多,業(yè)務(wù)系統(tǒng)與服務(wù)系統(tǒng)之間的調(diào)用,都需要有效管理。
在服務(wù)化的早期,服務(wù)不是很多,服務(wù)的注冊與發(fā)現(xiàn)并不是什么新鮮的名詞,Nginx+內(nèi)部域名服務(wù)器方式,甚至Nginx+host文件配置方式也能完成服務(wù)的注冊與發(fā)現(xiàn)。
架構(gòu)圖如下:
各組件角色如下:
Nginx通過多域名配置實(shí)現(xiàn)生產(chǎn)者服務(wù)路由,通過upstream對生產(chǎn)者提供負(fù)載均衡,通過checkhealth對生產(chǎn)者提供健康檢查。
在內(nèi)部域名服務(wù)器/本地host文件配置服務(wù)域名的方式,實(shí)現(xiàn)域名與服務(wù)的綁定。
生產(chǎn)者提供服務(wù)給消費(fèi)者訪問,并通過Nginx來進(jìn)行請求分發(fā)。在服務(wù)化的早期,服務(wù)較少,訪問量較小,解決了服務(wù)的注冊與發(fā)現(xiàn)與負(fù)載均衡等問題。隨著業(yè)務(wù)的發(fā)展,用戶量日益上升,服務(wù)也越來越多,該架構(gòu)的問題暴露出來:
1)最明顯的問題是所有的請求都需要nginx來轉(zhuǎn)發(fā),同時(shí)隨著訪問量的提升,會(huì)成為一個(gè)性能瓶頸。
2)隨著內(nèi)部服務(wù)的越來越多,服務(wù)上線nginx的配置,內(nèi)部域名的配置也越來越多,不利于快速部署。
3)一旦內(nèi)部網(wǎng)絡(luò)調(diào)整,nginx upstream也需要做對應(yīng)的配置調(diào)整。
Lazy服務(wù)注冊與發(fā)現(xiàn)(二)
由于所有的請求都需要nginx來轉(zhuǎn)發(fā),同時(shí)隨著訪問量的提升,會(huì)成為一個(gè)性能瓶頸,為了解決這個(gè)瓶頸,引入下面這個(gè)架構(gòu)。
這個(gè)架構(gòu)在nginx的上層加入了LVS,LVS基于第四層的IP轉(zhuǎn)發(fā),一定限度提高了并發(fā)能力,但是所有請求都通過LVS來轉(zhuǎn)發(fā)。
同時(shí)nginx分組,服務(wù)分組,雖然nginx不再是瓶頸的所在,但是這樣帶來的代價(jià)太高,配置越來越多,工作量越來越大,對系統(tǒng)壓力需要有預(yù)見性,有效nginx分組,服務(wù)分組部署。
Lazy服務(wù)注冊與發(fā)現(xiàn)(三)
由于所有的請求都需要LVS來轉(zhuǎn)發(fā),同時(shí)隨著訪問量的提升,會(huì)成為一個(gè)性能瓶頸,為了解決這個(gè)瓶頸,引入下面這個(gè)架構(gòu)。
在這個(gè)架構(gòu)基礎(chǔ)上需要做兩件事情:
對系統(tǒng)壓力需要有預(yù)見性,有效nginx分組,服務(wù)分組部署。
消費(fèi)端需要編程實(shí)現(xiàn)分組選擇,可以是輪訓(xùn),random等實(shí)現(xiàn),我們每一個(gè)消費(fèi)者同時(shí)承擔(dān)了的負(fù)載均衡的職責(zé)。
ZK服務(wù)注冊與發(fā)現(xiàn) (一)
通過架構(gòu)三解決了nginx的瓶緊,但是服務(wù)上下線需要在nginx,域名服務(wù)器做相應(yīng)的配置,一旦服務(wù)的IP端口發(fā)生變化,都需要在nginx上做相應(yīng)的配置,為了解決這個(gè)問題引入下面這個(gè)架構(gòu)。
服務(wù)在啟動(dòng)的時(shí)候就將endpoint注冊到Zookeeper對服務(wù)進(jìn)行統(tǒng)一管理。
服務(wù)節(jié)點(diǎn)增加Endpoint不需要做任何配置,ZK將以Watch機(jī)制通知消費(fèi)者。
消費(fèi)者本地緩存了提供者服務(wù)列表,不需要轉(zhuǎn)發(fā),直接發(fā)起服務(wù)調(diào)用。
缺點(diǎn):
需要通過zookeeper API來實(shí)現(xiàn)服務(wù)注冊與發(fā)現(xiàn),負(fù)載均衡,以及容錯(cuò),為了解決nginx的瓶緊架構(gòu)三也是需要通過編程的方式實(shí)現(xiàn)負(fù)載均衡。
ZK服務(wù)注冊與發(fā)現(xiàn) (二)
Zookeeper數(shù)據(jù)模型結(jié)構(gòu)是一個(gè)樹狀層次結(jié)構(gòu)。每個(gè)節(jié)點(diǎn)叫做Znode,節(jié)點(diǎn)可以擁有子節(jié)點(diǎn),同時(shí)允許將少量數(shù)據(jù)存儲(chǔ)在該節(jié)點(diǎn)下,客戶端可以通過NodeCacheListener監(jiān)聽節(jié)點(diǎn)的數(shù)據(jù)變更,PathChildrenCacheListener監(jiān)聽子節(jié)點(diǎn)變更來實(shí)時(shí)獲取Znode的變更(Wather機(jī)制)。
以下是點(diǎn)融成都服務(wù)注冊結(jié)構(gòu),見下圖,接下來的講解也將以這個(gè)結(jié)構(gòu)為基礎(chǔ):
ZK服務(wù)注冊與發(fā)現(xiàn) (三)
1./com/dianrong/cfg/1.0.0/rpcservice: 命名空間,用來跟其他用途區(qū)分。
2./com/dianrong/cfg/1.0.0/rpcservice下的所有子目錄由兩部分組成,
“應(yīng)用名稱” + “-” + ?“分組名稱”例如:ProductService-SG1,ProductService-SG2, 對應(yīng)Nginx注冊中心Nginx-SG1, Nginx-SG2
3. 服務(wù)分組下的所有子節(jié)點(diǎn)為臨時(shí)節(jié)點(diǎn),key為“PROVIDER”+ IP(去符號.) ?+ “-” + 端口,Value為endpoint信息。
例如:PROVIDER1921681010-8080 ? = http://192.168.10.10:8080
第二篇:代碼分析
項(xiàng)目說明
有了上面的理論我們接下來針對基于ZK的服務(wù)與發(fā)現(xiàn)的代碼分析,代碼已經(jīng)提交到git
GIT地址:
https://code.dianrong.com/projects/PLAT/repos/platform/compare/commits?sourceBranch=refs%2Fheads%2FEVER-81-zk&targetBranch=refs%2Fheads%2Fmaster
說明:
1. 該組件建立在Curator基礎(chǔ)之上,Curator是Netflix開源的一套ZooKeeper客戶端框架封裝ZooKeeper client與ZooKeeper server之間的連接處理。
2. Curator提供如下機(jī)制,保證我們不需要關(guān)注網(wǎng)絡(luò)通信,而把主要精力放在業(yè)務(wù)邏輯的處理。
重試機(jī)制:提供可插拔的重試機(jī)制, 它將給捕獲所有可恢復(fù)的異常配置一個(gè)重試策略, 并且內(nèi)部也提供了幾種標(biāo)準(zhǔn)的重試策略
連接狀態(tài)監(jiān)控: Curator初始化之后會(huì)一直的對zk連接進(jìn)行監(jiān)聽, 一旦發(fā)現(xiàn)連接狀態(tài)發(fā)生變化, 將作出相應(yīng)的處理
ZK客戶端實(shí)例管理:Curator對zk客戶端到server集群連接進(jìn)行管理. 并在需要的情況, 透明重建zk實(shí)例, 保證與zk集群的可靠連接
基于ZK的服務(wù)與發(fā)現(xiàn)UML類圖:
目標(biāo)
1. 統(tǒng)一配置中心
數(shù)據(jù)實(shí)時(shí)性,一旦zk節(jié)點(diǎn)發(fā)生變化,實(shí)時(shí)通知本地hash同步刷新。
2. 服務(wù)注冊
服務(wù)啟動(dòng)完成,服務(wù)IP,端口以臨時(shí)節(jié)點(diǎn)的形式注冊到zk,在網(wǎng)絡(luò)正常的情況下,一直存在。
3. 服務(wù)發(fā)現(xiàn)
服務(wù)啟動(dòng)完成,將服務(wù)注冊信息刷新到本地hash。
4. 服務(wù)上下線
服務(wù)注冊到zk將實(shí)時(shí)通知服務(wù)發(fā)現(xiàn)方,更新本地hash,服務(wù)下線也將實(shí)時(shí)通知服務(wù)發(fā)現(xiàn)方,更新本地hash。
5. 負(fù)載均衡
服務(wù)發(fā)現(xiàn)方獲取服務(wù)緩存在本地hash,通過random,robin等負(fù)載均衡算法完成服務(wù)選擇性調(diào)用。
6. 網(wǎng)絡(luò)中斷容災(zāi)
針對注冊方網(wǎng)絡(luò)中斷,服務(wù)下線,網(wǎng)絡(luò)恢復(fù),服務(wù)上線,并通知服務(wù)發(fā)現(xiàn)方更新本地Hash;
針對發(fā)現(xiàn)方網(wǎng)絡(luò)中斷,通過本地hash負(fù)載均衡,網(wǎng)絡(luò)恢復(fù)重刷hash,負(fù)載均衡重新分配。
7. Zookeeper宕機(jī)容災(zāi)
針對注冊方Zookeeper宕機(jī),服務(wù)下線,嘗試重連, Zookeeper 啟動(dòng)重連成功,服務(wù)上線,并通知服務(wù)發(fā)現(xiàn)方更新本地hash,針對發(fā)現(xiàn)方Zookeeper宕機(jī),通過本地hash負(fù)載均衡,嘗試重連, ?Zookeeper 啟動(dòng)重連成功,重刷hash,負(fù)載均衡重新分配。
基本配置
PathConfig.java
包含服務(wù)注冊的命名空間和統(tǒng)一配置的命名空間的配置。
SgConfig.java
包含服務(wù)名和分組名的配置
ZookeeperConfig.java
包含zookeeper地址,會(huì)話超時(shí)時(shí)間,連接超時(shí)時(shí)間,命名空間以及認(rèn)證信息的配置
ZK操作框架
IzookeeperManager.java
定義了一套zookeeper操作規(guī)范(類似JDBC操作數(shù)據(jù)數(shù)據(jù)庫規(guī)范),有待繼續(xù)完善。
ZookeeperManager.java
針對IzookeeperManager接口規(guī)范的實(shí)現(xiàn)(類似Mysql驅(qū)動(dòng)對Mysql操作的實(shí)現(xiàn))
ZookeeperManagerPool.java
針對ZookeeperManager實(shí)例的緩存,不同配置緩存不同ZookeeperManager實(shí)例,避免zookeeper連接創(chuàng)建的開銷,同時(shí)可以根據(jù)zookeeper水平分組擴(kuò)展zookeeper
實(shí)例。
統(tǒng)一配置,服務(wù)注冊與發(fā)現(xiàn)
AbstractZookeeperFeature.java
內(nèi)部兩個(gè)接口定義:
IConfigService 提供針對統(tǒng)一配置接口的定義, IManagementService提供服務(wù)注冊與發(fā)現(xiàn)接口的定義。
ConfigService.java
統(tǒng)一配置的實(shí)現(xiàn)。
ManagementService.java
服務(wù)注冊與發(fā)現(xiàn)的實(shí)現(xiàn)。
負(fù)載均衡
LbStrategy.java
負(fù)載均衡策略接口定義,目前實(shí)現(xiàn)了兩種負(fù)載均衡算法,Random負(fù)載均衡和Robin負(fù)載均衡。
RandomStrategy.java
基于隨機(jī)負(fù)載均衡的實(shí)現(xiàn)。
RobinStrategy.java
基于輪循負(fù)載均衡的實(shí)現(xiàn)。
統(tǒng)一配置Listener
ConfigPathChildrenCacheListener.java
統(tǒng)一配置結(jié)點(diǎn)監(jiān)聽, 針對CHILD_REMOVED,CHILD_ADDED,CHILD_UPDATED事件對本地hash實(shí)時(shí)更新。
服務(wù)注冊與發(fā)現(xiàn)Listener
ZookeeperStateListener.java
Zookeeper狀態(tài)監(jiān)聽接口定義,定義需要關(guān)心的三種事件:
LOST-斷開連接達(dá)到一定時(shí)間
CONNECTED-第一次連接成功
RECONNECTED-重連成功觸發(fā)事件。
ServiceRegistStateListener.java
服務(wù)注冊狀態(tài)監(jiān)聽實(shí)現(xiàn):
1.一旦網(wǎng)絡(luò)丟包嚴(yán)重/ zk宕機(jī)/ zk重啟,客戶端將會(huì)與zk斷開,服務(wù)下線,網(wǎng)絡(luò)恢復(fù)將觸發(fā)reconnected連接,服務(wù)重新注冊。
2.一旦zk斷開服務(wù)下線,長時(shí)間連接不上觸發(fā)Lost事件,ServiceRegistStateListener將會(huì)嘗試不斷連接直到連上為止,服務(wù)重新注冊。
ServiceDiscoverStateListener.java
服務(wù)發(fā)現(xiàn)狀態(tài)監(jiān)聽實(shí)現(xiàn):
1.一旦網(wǎng)絡(luò)丟包嚴(yán)重/ zk宕機(jī)/ zk重啟,客戶端將會(huì)與zk斷開,網(wǎng)絡(luò)恢復(fù)將觸發(fā)reconnected連接,重新獲取服務(wù)列表,刷新本地hash。
2.一旦zk斷開服務(wù)下線,長時(shí)間連接不上觸發(fā)Lost事件,ServiceDiscoverStateListener將會(huì)嘗試不斷連接直到連上為止,以便刷新本地hash。
ServicePathChildrenCacheListener.java
服務(wù)發(fā)現(xiàn)結(jié)點(diǎn)監(jiān)聽, 針對CHILD_REMOVED,CHILD_ADDED,CHILD_UPDATED事件消費(fèi)者對本地hash實(shí)時(shí)更新,以便及時(shí)刷新服務(wù)上下線。
第三篇:單元測試
環(huán)境準(zhǔn)備
1. 在zookeeper中準(zhǔn)備結(jié)點(diǎn) com/dianrong/cfg/1.0.0/rpcservice
create /com/dianrong/cfg/1.0.0/rpcservice ?“”
2. 模擬docker/JVM啟動(dòng)參數(shù)設(shè)置
由于點(diǎn)融網(wǎng)是通過物理機(jī)IP加端口映射到docker實(shí)例IP加端口的方式對外提供服務(wù),因此需要通過java -D配置宿主機(jī)的IP加端口以便應(yīng)用程序獲取服務(wù)IP加端口用來進(jìn)行服務(wù)注冊。
服務(wù)注冊
1. 在服務(wù)注冊之前通過zookeeper控制臺(tái)查看,期望的注冊節(jié)點(diǎn)目前為空。
2. 啟動(dòng)服務(wù)注冊單元測試,sleep 10s。
3. 觀察zookeeper中注冊結(jié)點(diǎn),期望的臨時(shí)節(jié)點(diǎn)已經(jīng)存在。
服務(wù)發(fā)現(xiàn)
通過服務(wù)注冊以后然后服務(wù)發(fā)現(xiàn),發(fā)現(xiàn)節(jié)點(diǎn)與預(yù)期注冊結(jié)點(diǎn)一致。
負(fù)載均衡
通過服務(wù)注冊以后然后負(fù)載均衡,負(fù)載均衡獲取節(jié)點(diǎn)與預(yù)期節(jié)點(diǎn)一致。
服務(wù)上下線
1. 準(zhǔn)備兩套單元測試并模擬docker/JVM啟動(dòng)參數(shù)設(shè)置。
2.啟動(dòng)服務(wù)注冊,服務(wù)發(fā)現(xiàn),負(fù)載均衡集成單元測試,測試控制臺(tái)按照預(yù)期打印出服務(wù)注冊信息,注冊了兩個(gè)服務(wù)節(jié)點(diǎn)通過負(fù)載均衡交叉出現(xiàn)。
3. 通過zookeeper控制臺(tái)查看成功注冊兩個(gè)節(jié)點(diǎn)。
4.關(guān)閉機(jī)器1單元測試,結(jié)點(diǎn)1在zookeeper離線,結(jié)點(diǎn)2在zookeeper在線。
負(fù)載均衡只有一個(gè)節(jié)點(diǎn)存在達(dá)到預(yù)期目的
網(wǎng)絡(luò)中斷
1. 啟動(dòng)服務(wù)注冊,服務(wù)發(fā)現(xiàn),負(fù)載均衡集成單元測試,測試控制臺(tái)按照預(yù)期打印出服務(wù)注冊信息。
2. 通過Linux iptables開啟防火墻,模擬網(wǎng)絡(luò)中斷。
服務(wù)掉線,通過zookeeper控制臺(tái)查看注冊節(jié)點(diǎn)消失。
服務(wù)發(fā)現(xiàn)方任然可以從本地Hash中獲取服務(wù)節(jié)點(diǎn)。
3. 關(guān)閉Iptables,服務(wù)上線。
服務(wù)上線,通過zookeeper控制臺(tái)查看注冊節(jié)點(diǎn)出現(xiàn)。
服務(wù)發(fā)現(xiàn)方獲取服務(wù)節(jié)點(diǎn)重刷本地Hash中。
Zookeeper宕機(jī)
1. 啟動(dòng)服務(wù)注冊,服務(wù)發(fā)現(xiàn),負(fù)載均衡集成單元測試,測試控制臺(tái)按照預(yù)期打印出服務(wù)注冊信息。
2.關(guān)閉zookeeper,單元測試出現(xiàn)連接拒絕錯(cuò)誤,但是任然能按照預(yù)期獲取本地hash中的服務(wù)注冊信息。
3. 啟動(dòng)zookeeper,單元測試打印出重連信息,并重刷本地服務(wù)hash。
本文作者:秦瑜 Chris.Qin(點(diǎn)融黑幫),來自點(diǎn)融BE Team, 2015年10月加入點(diǎn)融,多年大并發(fā)分布式互聯(lián)網(wǎng)架構(gòu)經(jīng)驗(yàn)。