馮戰(zhàn)勝,張 激,彭 宏,劉穎娜
(華東計算技術(shù)研究所,上海 201808)
在互聯(lián)網(wǎng)大行其道的今天,各種分布式系統(tǒng)已經(jīng)司空見慣.搜索引擎、電商網(wǎng)站、微博、微信、O2O平臺等,凡是涉及到大規(guī)模用戶、高并發(fā)訪問的,無一不是分布式[1].RPC作為分布式架構(gòu)的核心,一旦出現(xiàn)問題,整個分布式網(wǎng)絡(luò)就會出現(xiàn)癱瘓,RPC框架必須做到高可用才能保證分布式網(wǎng)絡(luò)的穩(wěn)定.ICE中間件作為一款跨語言開發(fā)的RPC框架,因其高效的性能和完整的服務(wù),被惠普等大型企業(yè)所采用.
Zookeeper是開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),許多分布式軟件都采用Zookeeper作為集群[2]管理,Zookeeper本身也支持高可用,隨著軟件版本的更新,Zookeeper的高效與穩(wěn)定很適合作為分布式環(huán)境下的高可用服務(wù)[3].
Zookeeper是Hadoop的正式子項目,是一個高效可靠的開源的分布式協(xié)調(diào)服務(wù)框架,由知名互聯(lián)網(wǎng)公司雅虎創(chuàng)建,是Google公司的分布式服務(wù)框架Chubby的開源實現(xiàn).在分布式環(huán)境下,Zookeeper可以保證順序一致性,原子性,可靠性和實時性.
Zookeeper本質(zhì)上是一個分布式的小文件存儲系統(tǒng),具有下述應(yīng)用特性:
(1)簡單.Zookeeper允許通過分布式程序共享方式,組織成類似標(biāo)準(zhǔn)文件系統(tǒng)層級命名空間協(xié)調(diào)的分布式程序.這個命名空間包含類似文件和目錄的數(shù)據(jù)寄存器,不像典型的用于存儲的文件系統(tǒng).Zookeeper的數(shù)據(jù)保存在內(nèi)存中,可實現(xiàn)高吞吐量和低延遲訪問.
(2)冗余.Zookeeper本身和其自身協(xié)調(diào)的程序一樣,設(shè)計為冗余的,在擁有許多主機集合的主機組上運行.組成Zookeeper服務(wù)群的所有服務(wù)器都已知對方,只要所有服務(wù)器中的主服務(wù)器可用,Zookeeper服務(wù)就是可用的.客戶端鏈接的一個Zookeeper服務(wù)器通過發(fā)送請求、獲取響應(yīng)、監(jiān)聽事件、發(fā)送心跳維持一個TCP(傳輸控制協(xié)議)鏈接,如果鏈接中斷,客戶端將連接到另外一個服務(wù)器
(3)有序.Zookeeper用一個反映所有 Zookeeper事務(wù)順序的數(shù)字,標(biāo)記每一個更新.以后的操作可以使用這個順序數(shù)字標(biāo)記.
(4)快速.Zookeeper在讀取占主要地位的負(fù)載環(huán)境下,在讀取操作比寫入操作更頻繁的情況下,讀取速度非常快.
(5)豐富的 API(應(yīng)用程序編程接口).Zookeeper為開發(fā)人員提供了一套豐富的API,減輕了開發(fā)人員編寫通用協(xié)議的負(fù)擔(dān)[4].
程序在啟動運行中總是需要配置文件,如果程序分散部署在多臺機器上,要逐個改變所在機器的配置文件就變得困難.現(xiàn)在可以把這些配置全部放到Zookeeper上去,保存在Zookeeper的某個目錄節(jié)點中,然后所有相關(guān)應(yīng)用程序?qū)@個目錄節(jié)點進(jìn)行監(jiān)聽,一旦配置信息發(fā)生變化,每個應(yīng)用程序就會收到Zookeeper的通知,然后從Zookeeper獲取新的配置信息應(yīng)用到系統(tǒng)中[5].基本過程如圖1.
圖1 Zookeeper的配置管理
所有機器約定在父目錄father下創(chuàng)建臨時目錄節(jié)點,然后Zookeeper監(jiān)聽父目錄節(jié)點的子節(jié)點的消息變化.一旦有機器出現(xiàn)問題,該機器與Zookeeper的連接就會斷開,其所創(chuàng)建的臨時目錄節(jié)點就會被刪除,所有的其他機器都會收到通知.
當(dāng)有新機器加入,就會在father父目錄下創(chuàng)建臨時節(jié)點,Zookeeper會把這個消息發(fā)送給父目錄下的所有機器,所有機器就會收到新機器加入的通知.如圖2.
圖2 Zookeeper的集群管理
Zookeeper的角色主要有3類:領(lǐng)導(dǎo)者(leader)、學(xué)習(xí)者(learner)和客戶(client),三者關(guān)系如圖3所示.領(lǐng)導(dǎo)者負(fù)責(zé)處理寫請求和系統(tǒng)狀態(tài)更新請求;學(xué)習(xí)者負(fù)責(zé)接收領(lǐng)導(dǎo)者發(fā)送的請求并對請求進(jìn)行投票,以及客戶端的讀寫請求;客戶端負(fù)責(zé)通過TCP鏈接向?qū)W習(xí)者發(fā)送請求和獲取響應(yīng).學(xué)習(xí)者又可細(xì)分為跟隨者(follower)和觀察者 (observer),兩者的區(qū)別為:觀察者只同步領(lǐng)導(dǎo)者的狀態(tài),但不參與領(lǐng)導(dǎo)者失效時的競選和投票[6].
圖3 Zookeeper的角色關(guān)系
當(dāng)領(lǐng)導(dǎo)者失去響應(yīng)時,進(jìn)入恢復(fù)模式(競選),某服務(wù)器推薦自己作為領(lǐng)導(dǎo)者,將消息發(fā)送給集群,等待其它服務(wù)器接收該舉薦信息.若同意,向領(lǐng)導(dǎo)者服務(wù)器發(fā)送同意消息;若不同意,重復(fù)自薦流程,直到獲得N/2+1票數(shù)后,才被認(rèn)定為領(lǐng)導(dǎo)者,整個競選流程結(jié)束.具體流程如下:
首先,某臺服務(wù)器發(fā)起選舉線程,選舉線程向所有服務(wù)器發(fā)起一次詢問.當(dāng)收到回復(fù),選舉線程驗證zxid(Zookeeper transaction ID),判斷是否為自己發(fā)起的詢問,并獲 取對方id,同時保存到此次選舉投票記錄表中.當(dāng)收到所有服務(wù)器回復(fù)后,選舉線程計算并選出zxid值最大的服務(wù)器,將該服務(wù)器的信息,設(shè)置為被推薦的領(lǐng)導(dǎo)者.被推薦的領(lǐng)導(dǎo)者發(fā)起選舉,向其它服務(wù)器發(fā)送PROPOSAL消息,其它服務(wù)器接收該消息后,進(jìn)行投票;若同意,則發(fā)送 ACK 消息.此時,被推薦的領(lǐng)導(dǎo)者判斷是否有超過N/2+1的服務(wù)器同意;若超過,則被推薦的領(lǐng)導(dǎo)者獲勝;否則,重復(fù)流程,直到領(lǐng)導(dǎo)者被選出.
客戶端在所關(guān)心的目錄節(jié)點上注冊Watcher,當(dāng)被監(jiān)聽的節(jié)點狀態(tài)發(fā)生變化時(即數(shù)據(jù)改變、被刪除、子目錄節(jié)點增加或刪除時),會觸發(fā)WatchedEvent事件,并發(fā)送給對其進(jìn)行監(jiān)聽的客戶端.Watcher機制可以保證整個集群系統(tǒng)中各個服務(wù)器之間的通信和協(xié)調(diào),實現(xiàn)對數(shù)據(jù)變更的實時處理.Zookeeper所提供的基本操作包括:create(創(chuàng)建節(jié)點)、delete(刪除節(jié)點)、setData(設(shè)置節(jié)點數(shù)據(jù))、getData(獲取節(jié)點數(shù)據(jù))、exists(查看節(jié)點是否存在)、以及getChildren(獲取子節(jié)點)等.其中,exists、getChildren、getData 操作可以在節(jié)點上注冊 Watcher;而 create,delete,setData 操作可以觸發(fā)設(shè)置在節(jié)點上的Watcher[7].
在該機制中,服務(wù)端存儲事件的信息,客戶端存儲事件的信息和Watcher的執(zhí)行邏輯.當(dāng)在某節(jié)點上注冊Watcher事件時,客戶端線程向服務(wù)器注冊Watcher;同時,在Watcher對象管理器中存儲Watcher對象以及對應(yīng)的節(jié)點路徑.當(dāng)某節(jié)點觸發(fā)Watcher事件時,服務(wù)器向?qū)?yīng)的客戶端發(fā)送通知(包含事件類型和節(jié)點路徑),該客戶端線程從Watcher對象管理器中根據(jù)映射關(guān)系取出對應(yīng)的Watcher對象,執(zhí)行回調(diào)函數(shù).其中,回調(diào)函數(shù)包含WatchEvent類型的參數(shù),該類型參數(shù)(WatchEvent)封裝事件的通知狀態(tài)(keeperState)、事件類型(EventType)和節(jié)點路徑(path),方便回調(diào)函數(shù)對事件進(jìn)行處理.
ICE是一種新的面向?qū)ο蟮闹虚g件技術(shù).從根本上說,ICE為構(gòu)建面向?qū)ο蟮目蛻簦?wù)器應(yīng)用提供了工具、API和庫支持[8].可用于替代像CORBA或COM/DCOM/COM+這樣的中間件,為構(gòu)建面向?qū)Φ姆植际娇蛻舴?wù)器應(yīng)用提供了平臺支持.ICE應(yīng)用適合在異種環(huán)境中使用:客戶和服務(wù)器可以用不同的編程語言編寫,可以運行在不同的操作系統(tǒng)和機器架構(gòu)上,并且可以使用多種網(wǎng)絡(luò)技術(shù)進(jìn)行通信.在最新的3.7版本中,支持 c++,c#,.Net,java,javascript,objective-c,python,ruby 等編程語言,操作系統(tǒng)支持 windows,linux 和 mac os,支持最新的 docker部署,并且無論部署環(huán)境如何,這些應(yīng)用的源碼都是可移植的.
ICE定義了自己的規(guī)范語言Slice,它用于使對象接口與其實現(xiàn)相分離的基礎(chǔ)性抽象機制.客戶和服務(wù)器可以用不同的編程語言編寫,可以運行在不同的操作系統(tǒng)和機器架構(gòu)上,并且可以使用多 種網(wǎng)絡(luò)協(xié)議進(jìn)行高效通信.
ICE客戶與服務(wù)器內(nèi)部的邏輯結(jié)構(gòu)如圖4所示,ICE核心為遠(yuǎn)地通信提供了客戶端和服務(wù)器端運行時支持,ICE核心是作為客戶和服務(wù)器可與之鏈接的庫提供的,客戶ICE核心、服務(wù)器ICE核心和對象適配來自于庫代碼,代理和骨架由Slice定義生成.客戶應(yīng)用和服務(wù)器應(yīng)用來自于程序員編寫的上層應(yīng)用代碼[9].
IceGrid是ICE的核心服務(wù),提供了若干基本的服務(wù),可以簡化分布式應(yīng)用的開發(fā)和部署[9].IceGrid主要由registry和node組成,registry主要用來為客戶端提供定位服務(wù),并且管理部署的node節(jié)點,node用來裝載服務(wù),根據(jù)registry的指示對服務(wù)進(jìn)行部署與管理.
定位服務(wù):保證服務(wù)節(jié)點的位置透明性.當(dāng)客戶端需要訪問某一個服務(wù)時,不需要了解服務(wù)的具體位置,直接訪問registry,由registry將服務(wù)的具體位置告訴客戶端,做到服務(wù)端的位置透明.
圖4 客戶與服務(wù)器的結(jié)構(gòu)
服務(wù)管理:管理服務(wù)的注冊,啟動與關(guān)閉.所有的服務(wù)都需要向registry進(jìn)行注冊,當(dāng)服務(wù)需要被訪問時,由registry通知相應(yīng)的node對服務(wù)進(jìn)行啟動與關(guān)閉的管理,服務(wù)的負(fù)載均衡也由registry進(jìn)行控制,整個網(wǎng)絡(luò)服務(wù)的部署也由registry進(jìn)行管理.registry是整個網(wǎng)絡(luò)的樞紐.
IceGrid作為整個ICE網(wǎng)絡(luò)服務(wù)的骨架,起著至關(guān)重要的作用,所有的分布式網(wǎng)絡(luò)服務(wù)都通過IceGrid進(jìn)行訪問,當(dāng)整個網(wǎng)絡(luò)比較龐大時,單個registry已經(jīng)不能滿足網(wǎng)絡(luò)的負(fù)載需求,當(dāng)registry出現(xiàn)問題時,整個網(wǎng)絡(luò)就會癱瘓.所以registry本身支持主從模式的冗余.
首先啟動master.Master的配置文件與slave的配置文件有所不同,master中必須指定registry的名字為master,以和其他slave進(jìn)行區(qū)分.
遠(yuǎn)程將部署文件discriptor發(fā)送給master.當(dāng)master收到部署文件后,會等待node節(jié)點和slave的注冊.
啟動所有的slave.當(dāng)slave啟動后,會主動向master進(jìn)行注冊,注冊的過程中,master會將部署文件發(fā)送給所有的slave,在slave與master之間,實現(xiàn)了一個發(fā)布訂閱服務(wù),當(dāng)master對部署文件進(jìn)行更新時,會通知所有的slave進(jìn)行部署文件同步.
啟動所有的node節(jié)點.當(dāng)node節(jié)點啟動后,會首先向master進(jìn)行注冊,隨后master將相應(yīng)的部署配置文件發(fā)給node,并將所有slave的列表也發(fā)給node,node通過部署文件啟動相應(yīng)的服務(wù).然后向所有的slave進(jìn)行注冊.
當(dāng)服務(wù)部署完成后,master和slave都可以給客戶端提供服務(wù),都可以對node進(jìn)行管理,但是只能通過master對部署文件進(jìn)行更改,slave對部署文件是只讀模式.
(1)主從切換不能自動進(jìn)行
當(dāng)master出現(xiàn)問題時,slave不能主動進(jìn)行選舉切換到 master,如果 master出現(xiàn)問題,slave雖然可以繼續(xù)提供服務(wù),但是部署文件不能進(jìn)行更新,一旦需要更新部署文件,就必須更改某一個slave的配置為master,使用新的配置重新啟動這個slave,當(dāng)整個網(wǎng)絡(luò)十分龐大復(fù)雜時,會增加運維的成本.
(2)registry要實現(xiàn)的功能太多
registry不僅要提供位置服務(wù),服務(wù)管理,還需要實現(xiàn)發(fā)布訂閱服務(wù)對部署文件進(jìn)行同步管理.當(dāng)部署文件有更新時,master要通知所有的slave,并且將部署文件同步到所有的slave中,當(dāng)某個slave因為網(wǎng)絡(luò)問題斷開連接重新與master建立連接時,master要重新與slave同步數(shù)據(jù)庫文件.當(dāng)網(wǎng)絡(luò)負(fù)載很大時,registry要做的事情太多,將這么多功能放在一起,很容易造成網(wǎng)絡(luò)故障,進(jìn)而加劇網(wǎng)絡(luò)環(huán)境的進(jìn)一步惡化.
Zookeeper作為一款開源多年的分布式協(xié)調(diào)服務(wù)框架,支持高可用,本身非常可靠.通過利用 Zookeeper的配置管理和集群管理功能,可以解決registry中遇到的問題.
整體的Zookeeper目錄結(jié)構(gòu)圖如圖5,其中根節(jié)點為“/IceGrid”,下一級包含 2 個目錄節(jié)點:“/Configuration”,“/Registrys”.
(1)利用集群管理解決主從切換問題
在Zookeeper中,首先建立持久化目錄節(jié)點(PERSISTENT)“/registrys”,稱為父目錄,用于集中化管理所有的registry服務(wù)器,每一個registry均被創(chuàng)建為父目錄下的臨時目錄節(jié)點(EPHEMERAL),稱為子目錄,并在父目錄下注冊Watcher.當(dāng)某一個registry宕機或因網(wǎng)絡(luò)問題離開集群時,其會話(session)過期,對應(yīng)的臨時目錄節(jié)點被自動刪除.根據(jù)Watcher機制,當(dāng)被監(jiān)聽的父目錄節(jié)點狀態(tài)發(fā)生變化時,即父目錄節(jié)點下的子目錄節(jié)點被刪除,就會觸發(fā)WatchedEvent事件,關(guān)注該父目錄節(jié)點的所有registry均被通知.若失去響應(yīng)的 registry 恰為 master時,根據(jù)選舉機制,Zookeeper服務(wù)器進(jìn)入恢復(fù)模式(競選),觸發(fā)選舉流程,選出新的master.同理,當(dāng)父目錄節(jié)點的子目錄節(jié)點增加時,關(guān)注該父目錄節(jié)點的所有registry均被通知,且根據(jù)選舉機制,Zookeeper服務(wù)器進(jìn)入廣播模式 (同步),該新加入的registry與master進(jìn)行狀態(tài)同步.
圖5 Zookeeper目錄結(jié)構(gòu)圖
因此,利用 Watcher機制和選舉機制,可解決IceGrid中registry的主從切換問題.當(dāng)master出現(xiàn)問題,由 Zookeeper選出新的 master,當(dāng)有新的 registry 加入時,由Zookeeper通知master與新的registry進(jìn)行狀態(tài)同步.
(2)利用配置管理解決部署文件同步問題
在IceGrid中,由于registry集群中的各個服務(wù)節(jié)點,均采用同一套部署文件;更新部署文件只能通過master進(jìn)行,隨后master需要通知所有的slave進(jìn)行部署文件同步,同步期間,所有的slava需要與master進(jìn)行讀寫操作.
依據(jù)Zookeeper節(jié)點特有的數(shù)據(jù)存儲能力,以及Watcher機制,可以實現(xiàn)部署文件信息的統(tǒng)一管理.將整個registry的部署信息存儲在持久化目錄節(jié)點(PERSISTENT)“/Configuration”上,集群中所有registry作為Zookeeper客戶端 ,在該節(jié)點上注冊Watcher.當(dāng)該節(jié)點上的數(shù)據(jù)發(fā)生更改(即部署信息被管理員修改),觸發(fā)設(shè)置在該節(jié)點上的Watcher.所有關(guān)注該節(jié)點的客戶端均被通知,并根據(jù)收到的通知更新自身部署信息,實現(xiàn)與master部署信息的同步.這樣,每當(dāng)部署信息被修改,不需要master對slave進(jìn)行通知,由 Zookeeper的 watcher機制進(jìn)行通知,部署文件的同步也由Zookeeper進(jìn)行同步,master只需要專注于提供其他服務(wù)就好,這樣也不需要master與slave進(jìn)行頻繁的讀寫操作,降低master的讀寫壓力.所有的I/O讀寫全部由Zookeeper集群提供.
通過在3臺服務(wù)器上部署3個IceRegistry服務(wù),分別取名為r1,r2,r3這3臺服務(wù)器都向Zookeeper集群進(jìn)行注冊,在首次注冊中,r2 為主節(jié)點,r1 和 r3 為從節(jié)點,期間斷開主節(jié)點r2,然后通過Zookeeper的配置管理對部署文件進(jìn)行更新,發(fā)現(xiàn)部署信息仍能在余下的r1和r3中進(jìn)行同步,通過Zookeeper的shell命令查看節(jié)點信息,發(fā)現(xiàn)r1被選為了新的主節(jié)點,IceGrid部署的服務(wù)更新成功.
然后重新開啟r2,再通過shell對Zookeeper的文件系統(tǒng)進(jìn)行查看,r2又重新向Zookeeper進(jìn)行注冊,并且部署文件也自動同步到了r2的數(shù)據(jù)庫中.
通過以上的集群搭建與測試,說明IceRegistry的主從節(jié)點可以自動切換,不再需要人工干預(yù).并且不再需要主節(jié)點來同步部署信息或者發(fā)現(xiàn)新的從節(jié)點,這些責(zé)任全部由Zookeeper集群來做.
通過將Zookeeper加入到IceGrid框架中,利用Zookeeper的集群管理可以解決registry不能自動主從切換的問題,利用Zookeeper的配置管理可以解決registry之間的部署文件同步問題,改進(jìn)后的IceGrid將主從切換和部署文件同步等功能剝離到了Zookeeper中,這樣不僅解決了IceGrid之前存在的主從切換的缺陷問題,也減輕了registry需要承擔(dān)的任務(wù),可以更可靠的為client提供定位服務(wù).
本文就ICE的主從切換方面進(jìn)行了探索,提出了一種基于Zookeeper的解決方案,隨著中間件技術(shù)的不斷進(jìn)步,還有更多優(yōu)秀的中間件加入到分布式應(yīng)用當(dāng)中來.因此基于中間件的架構(gòu)模式會隨著廣泛應(yīng)用而不斷成熟,中間件技術(shù)會更加適應(yīng)復(fù)雜的網(wǎng)絡(luò)環(huán)境和繁雜的應(yīng)用場景.