基于流言協(xié)議的服務(wù)發(fā)現(xiàn)存儲(chǔ)倉(cāng)庫(kù)設(shè)計(jì)

前言

隨著近年來(lái)微服務(wù)理論越來(lái)越流行份乒,其基礎(chǔ)之一的服務(wù)發(fā)現(xiàn)也越來(lái)越受到人們的重視半沽。傳統(tǒng)的單點(diǎn)服務(wù)倉(cāng)庫(kù)受限于不易擴(kuò)展砂代、容災(zāi)麻煩等缺點(diǎn)的考慮已不再適用于復(fù)雜的集群系統(tǒng)。目前來(lái)說(shuō),大多數(shù)的成熟服務(wù)發(fā)現(xiàn)系統(tǒng)采用的是zookeeper琳拭、etcd或是consul作為服務(wù)存儲(chǔ)倉(cāng)庫(kù)训堆,如kubernetes用的就是etcd。雖然以上三種組件內(nèi)部協(xié)議和功能側(cè)重點(diǎn)各不相同白嘁,但最終都實(shí)現(xiàn)了一個(gè)具有強(qiáng)一致性坑鱼、高可用性、分布式的存儲(chǔ)系統(tǒng)絮缅。對(duì)于這樣的存儲(chǔ)系統(tǒng)鲁沥,分布式支持服務(wù)發(fā)現(xiàn)可用水平擴(kuò)展、高可用性可以保證了服務(wù)發(fā)現(xiàn)的穩(wěn)定性耕魄,KV系統(tǒng)存儲(chǔ)了服務(wù)數(shù)據(jù)黍析,而唯一有質(zhì)疑的是強(qiáng)一致性。下面我們要探討一個(gè)問題屎开,究竟服務(wù)發(fā)現(xiàn)是否真的需要強(qiáng)一致性?網(wǎng)上有一篇文章《為什么不要把ZooKeeper用于服務(wù)發(fā)現(xiàn)》探討了這個(gè)問題马靠。有興趣的同學(xué)可以看一下奄抽。
 其實(shí)強(qiáng)一致性有兩個(gè)方面限制了服務(wù)發(fā)現(xiàn):

  • zookeeper這類組件的強(qiáng)一致性是有代價(jià)的:即在一個(gè)2n+1個(gè)節(jié)點(diǎn)組成的集群中,一旦少于n+1個(gè)節(jié)點(diǎn)工作甩鳄,就不可用逞度。假設(shè)一個(gè)服務(wù)發(fā)現(xiàn)集群有5個(gè)節(jié)點(diǎn),當(dāng)3個(gè)以上節(jié)點(diǎn)出現(xiàn)故障時(shí)妙啃,整個(gè)集群也就無(wú)法提供服務(wù)了档泽。拿CAP理論來(lái)說(shuō),就是犧牲了A(可用性)換取了C(一致性)揖赴,但對(duì)于服務(wù)發(fā)現(xiàn)系統(tǒng)馆匿,一般可以接受返回幾次錯(cuò)誤數(shù)據(jù),但是決不能容忍服務(wù)停擺的燥滑。
  • zookeeper這類組件雖然可以支持?jǐn)U展渐北,但受自身實(shí)現(xiàn)一致性協(xié)議的限制,擴(kuò)展節(jié)點(diǎn)數(shù)目越多铭拧,為達(dá)到半數(shù)的通信成本也越高赃蛛,因此是無(wú)法實(shí)現(xiàn)無(wú)限擴(kuò)展的。

本文介紹了一種基于流言協(xié)議的KV存儲(chǔ)系統(tǒng)搀菩,該系統(tǒng)具有最終一致性呕臂、分布式、高可用的特點(diǎn)肪跋,可以有效的運(yùn)用與服務(wù)發(fā)現(xiàn)系統(tǒng)歧蒋。

服務(wù)倉(cāng)庫(kù)工作示意圖

流言協(xié)議

流言協(xié)議是一個(gè)去中心化的通信協(xié)議,當(dāng)某節(jié)點(diǎn)收到一個(gè)新事件時(shí)會(huì)有以下的操作:

  • 隨機(jī)的選擇其他n個(gè)節(jié)點(diǎn)作為傳輸對(duì)象。
  • 若其他節(jié)點(diǎn)第一次收到該事件疏尿,則隨機(jī)選擇n個(gè)節(jié)點(diǎn)轉(zhuǎn)發(fā)瘟芝。
  • 每個(gè)收到事件信息的節(jié)點(diǎn)重復(fù)完成上述工作。

流言協(xié)議主要實(shí)現(xiàn)了兩個(gè)功能:節(jié)點(diǎn)存活檢查和消息傳遞褥琐。常用的檢測(cè)節(jié)點(diǎn)存活技術(shù)是心跳協(xié)議锌俱,即定時(shí)發(fā)出心跳包來(lái)證明自己存活。但這樣的心跳協(xié)議有很多缺點(diǎn)敌呈,首先如何是確保心跳包準(zhǔn)確送達(dá)目標(biāo)節(jié)點(diǎn)贸宏,如果網(wǎng)絡(luò)問題導(dǎo)致心跳包沒有正確送達(dá),會(huì)不會(huì)誤傷健康的節(jié)點(diǎn)磕洪?這個(gè)雖然可以通過(guò)協(xié)議來(lái)解決但同時(shí)導(dǎo)致了協(xié)議的復(fù)雜度吭练。其次心跳協(xié)議是一個(gè)端到端的協(xié)議,如何應(yīng)用與大規(guī)模的集群中析显?采用星型拓?fù)浣Y(jié)構(gòu)會(huì)帶來(lái)單點(diǎn)問題鲫咽;采用多對(duì)多拓?fù)潆S著節(jié)點(diǎn)數(shù)目過(guò)多帶來(lái)通訊成本急劇上升;采用環(huán)狀拓?fù)鋾?huì)因?yàn)橥瑫r(shí)兩處節(jié)點(diǎn)故障導(dǎo)致不可用谷异。消息傳遞同樣面臨這存活檢測(cè)遇到上述問題分尸,如何保證數(shù)據(jù)準(zhǔn)確傳達(dá)和選擇通訊拓?fù)浣Y(jié)構(gòu)?對(duì)此流言協(xié)議可以很好的解決這些問題歹嘹。


不靠譜的拓?fù)浣Y(jié)構(gòu)

"SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"箩绍。這篇論文詳細(xì)描述了流言協(xié)議實(shí)現(xiàn)方式:

  • 采用兩次檢查的存活協(xié)議,節(jié)點(diǎn)定時(shí)ping已知任一節(jié)點(diǎn)尺上,如果沒有收到ack材蛛,則轉(zhuǎn)發(fā)請(qǐng)求給k個(gè)節(jié)點(diǎn),讓這些節(jié)點(diǎn)發(fā)送ping給被檢測(cè)節(jié)點(diǎn)怎抛,若這些節(jié)點(diǎn)都沒有收到ack就認(rèn)為被檢測(cè)節(jié)點(diǎn)有問題卑吭。
  • 需要傳輸?shù)臄?shù)據(jù)作為ping和ack包的附帶數(shù)據(jù)一起傳輸,有效減少傳輸次數(shù)马绝。

此文章同時(shí)也論證了流言協(xié)議兩個(gè)特點(diǎn):

  • 對(duì)于每個(gè)節(jié)點(diǎn)來(lái)說(shuō)陨簇,存活檢查需要的時(shí)間和數(shù)據(jù)載荷大小與集群規(guī)模無(wú)關(guān)。(成本固定)
  • 對(duì)于一個(gè)n個(gè)節(jié)點(diǎn)的集群迹淌,消息傳遞到全部節(jié)點(diǎn)的時(shí)間是K*log(n)河绽。(快速收斂)
流言協(xié)議的傳播速度

服務(wù)發(fā)現(xiàn)的KV存儲(chǔ)系統(tǒng)

現(xiàn)在我們需要設(shè)計(jì)一個(gè)用于服務(wù)發(fā)現(xiàn)的KV存儲(chǔ)系統(tǒng),考慮到實(shí)踐運(yùn)用場(chǎng)合唉窃,該系統(tǒng)應(yīng)該具備:

  • 分布式:存儲(chǔ)系統(tǒng)由多個(gè)節(jié)點(diǎn)構(gòu)成耙饰。
  • 高可用:每個(gè)存活的節(jié)點(diǎn)都能提供服務(wù)。
  • 最終一致性:任一節(jié)點(diǎn)上的數(shù)據(jù)變化最終體現(xiàn)在各個(gè)節(jié)點(diǎn)上纹份。由于服務(wù)啟停是個(gè)頻率較低的過(guò)程苟跪,服務(wù)發(fā)現(xiàn)系統(tǒng)可以忍受幾秒的數(shù)據(jù)的不一致廷痘,但要求最終提供的服務(wù)數(shù)據(jù)是一致的。
  • 狀態(tài)檢測(cè):新增或是移除節(jié)點(diǎn)件已,亦或是節(jié)點(diǎn)故障笋额,其他節(jié)點(diǎn)必須知悉,并對(duì)應(yīng)新增或刪減數(shù)據(jù)篷扩。

我們不難發(fā)現(xiàn)分布式兄猩、高可用和狀態(tài)檢測(cè)這些特性流言協(xié)議已經(jīng)實(shí)現(xiàn)了。無(wú)論集群的節(jié)點(diǎn)數(shù)目有多少鉴未,每個(gè)節(jié)點(diǎn)只要付出固定的成本就可以實(shí)現(xiàn)快速的存活檢測(cè)和數(shù)據(jù)傳輸枢冤。這樣我們可以構(gòu)建這樣的模型:

  • 集群中的每個(gè)節(jié)點(diǎn)各自維護(hù)一個(gè)KV存儲(chǔ)齐佳。
  • 對(duì)節(jié)點(diǎn)KV數(shù)據(jù)的修改都會(huì)通過(guò)流言協(xié)議擴(kuò)散到集群其他節(jié)點(diǎn)宏怔。
  • 節(jié)點(diǎn)收到其他節(jié)點(diǎn)數(shù)據(jù)增減消息后會(huì)對(duì)自身的KV存儲(chǔ)進(jìn)行增減操作并擴(kuò)散疹启。
  • 新增節(jié)點(diǎn)在加入集群后立刻將本節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)通過(guò)流言協(xié)議同步給集群其他節(jié)點(diǎn)贝奇。

但是這樣模型還是有些問題。假設(shè)兩個(gè)不同節(jié)點(diǎn)同時(shí)對(duì)一個(gè)Key值的數(shù)據(jù)進(jìn)行修改放祟,那對(duì)于集群的其他節(jié)點(diǎn)來(lái)說(shuō)叔收,究竟應(yīng)該采用哪個(gè)節(jié)點(diǎn)的數(shù)據(jù)尝江?這種情況會(huì)導(dǎo)致喪失最終一致性啸驯。我們發(fā)現(xiàn)僅僅依靠流言協(xié)議是無(wú)法實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的客扎,還要做一些其他的工作。
 針對(duì)服務(wù)發(fā)現(xiàn)這個(gè)特殊場(chǎng)合坯汤,我們不難發(fā)現(xiàn)一個(gè)特點(diǎn):每個(gè)節(jié)點(diǎn)只會(huì)主動(dòng)增減和自己相關(guān)的數(shù)據(jù)。也就是說(shuō)A節(jié)點(diǎn)上服務(wù)發(fā)生變化只會(huì)讓A節(jié)點(diǎn)增減數(shù)據(jù)搀愧,其他節(jié)點(diǎn)只是被動(dòng)的接收A節(jié)點(diǎn)的數(shù)據(jù)變化惰聂。A節(jié)點(diǎn)上的數(shù)據(jù),A節(jié)點(diǎn)具有最終的發(fā)言權(quán)咱筛!為了解決不同節(jié)點(diǎn)同時(shí)寫帶來(lái)的數(shù)據(jù)不一致問題搓幌,我們加上兩條規(guī)定:

  • 每個(gè)節(jié)點(diǎn)只能修改屬于自己的數(shù)據(jù),然后再將數(shù)據(jù)同步給集群其他節(jié)點(diǎn)迅箩。
  • 當(dāng)某個(gè)節(jié)點(diǎn)故障或脫離集群時(shí)溉愁,其他的節(jié)點(diǎn)刪除與此節(jié)點(diǎn)相關(guān)數(shù)據(jù)。


    數(shù)據(jù)同步模型

但這并不能完全解決最終一致性的問題饲趋。流言協(xié)議不能保證傳輸?shù)捻樞蛐怨战遥簿褪钦f(shuō)假設(shè)A節(jié)點(diǎn)先發(fā)送了消息a=1,再發(fā)送a=2奕塑。B節(jié)點(diǎn)可能會(huì)先收到a=2再收到a=1堂污。消息傳輸完畢后,我們發(fā)現(xiàn)A節(jié)點(diǎn)中a=2龄砰,B節(jié)點(diǎn)a=1盟猖,失去了最終一致性讨衣。其實(shí)解決這種只有唯一寫者的順序不一致問題很簡(jiǎn)單,只需要對(duì)每個(gè)消息添加一個(gè)版本號(hào)就可以式镐。只有收到的消息比當(dāng)前數(shù)據(jù)版本號(hào)新才修改數(shù)據(jù)反镇。在這新增兩條規(guī)則:

  • 每個(gè)節(jié)點(diǎn)維護(hù)一個(gè)版本號(hào),當(dāng)修改數(shù)據(jù)時(shí)增加此版本號(hào)娘汞。其他節(jié)點(diǎn)收到同步消息時(shí)對(duì)比消息的版本號(hào)和自身數(shù)據(jù)的版本號(hào)歹茶,只有發(fā)現(xiàn)消息版本號(hào)大于數(shù)據(jù)版本號(hào)才對(duì)數(shù)據(jù)進(jìn)行修改。
  • 刪除數(shù)據(jù)時(shí)并不直接刪除數(shù)據(jù)价说,而是記錄下此時(shí)的版本號(hào)辆亏,標(biāo)記此數(shù)據(jù)刪除,防止過(guò)時(shí)消息污染數(shù)據(jù)鳖目。經(jīng)過(guò)一段時(shí)間才回收被刪除數(shù)據(jù)扮叨。

到這里看起來(lái)已經(jīng)解決了最終一致性的問題,但是在實(shí)踐運(yùn)用場(chǎng)合我們發(fā)現(xiàn)這樣的問題领迈。如圖在T3時(shí)刻彻磁,A節(jié)點(diǎn)發(fā)生崩潰重啟再加入集群,由于重啟時(shí)間較短狸捅,集群其他節(jié)點(diǎn)不認(rèn)為此節(jié)點(diǎn)失活因此保留A節(jié)點(diǎn)相關(guān)數(shù)據(jù)衷蜓。但新啟動(dòng)的A節(jié)點(diǎn)并不知道崩潰前的最新版本號(hào),還是從版本號(hào)1開始同步數(shù)據(jù)尘喝。這個(gè)同步消息自然遭到目前版本是3的B節(jié)點(diǎn)的拒絕磁浇,至此最終一致性喪失。


節(jié)點(diǎn)崩潰導(dǎo)致最終一致性喪失

 解決這個(gè)問題有兩個(gè)思路朽褪,一個(gè)是實(shí)時(shí)將節(jié)點(diǎn)最新的版本號(hào)記錄在本地文件或是外部存儲(chǔ)器中置吓,這樣即使崩潰重啟也能恢復(fù)最新版本號(hào)。另一種方法比較簡(jiǎn)單缔赠,按照之前的原則”節(jié)點(diǎn)具有本節(jié)點(diǎn)數(shù)據(jù)的最終發(fā)言權(quán)“衍锚,當(dāng)A節(jié)點(diǎn)發(fā)現(xiàn)屬于自己節(jié)點(diǎn)的數(shù)據(jù)版本號(hào)竟然比自己當(dāng)前版本號(hào)還大時(shí),就立即明白本節(jié)點(diǎn)的版本號(hào)已經(jīng)過(guò)時(shí)了嗤堰。這樣將本節(jié)點(diǎn)版本號(hào)置為數(shù)據(jù)版本號(hào)+1戴质,再同步數(shù)據(jù),就解決了這個(gè)問題踢匣。如圖雖然T4時(shí)刻同步失敗了告匠,當(dāng)T5時(shí)刻收到B節(jié)點(diǎn)轉(zhuǎn)發(fā)的數(shù)據(jù)后,A節(jié)點(diǎn)更新了自己的版本號(hào)离唬,只后同步數(shù)據(jù)就OK了凫海。在此新增一條規(guī)則:

  • 節(jié)點(diǎn)收到屬于自己的數(shù)據(jù)消息且發(fā)現(xiàn)版本號(hào)比自己版本號(hào)大時(shí),更新自己的版本號(hào)男娄。


    更新過(guò)時(shí)版本號(hào)

通過(guò)以上的幾條規(guī)則行贪,我們成功的在流言協(xié)議的基礎(chǔ)上實(shí)現(xiàn)了最終數(shù)據(jù)一致性漾稀,從而完成了基于服務(wù)發(fā)現(xiàn)KV存儲(chǔ)的設(shè)計(jì)。在實(shí)際使用過(guò)程中建瘫,服務(wù)端嵌入KV存儲(chǔ)來(lái)實(shí)時(shí)更新集群可用服務(wù)崭捍,客戶端既可以通過(guò)嵌入KV存儲(chǔ)也可以通過(guò)查詢嵌入KV存儲(chǔ)的DNS服務(wù)器來(lái)查詢服務(wù)。


基于流言協(xié)議的服務(wù)發(fā)現(xiàn)框架

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

實(shí)現(xiàn)流言協(xié)議并不是一件輕松的事情啰脚,幸好hashicorp的memberlist組件已經(jīng)幫我們實(shí)現(xiàn)了流言協(xié)議主要功能殷蛇。memberlist的流言傳播用的是udp協(xié)議。這帶來(lái)一個(gè)問題橄浓,眾所周知udp是一個(gè)一個(gè)包傳輸?shù)牧C危瑪?shù)據(jù)信息作為ping或ack的附加載荷受限與udp包的大小,這限制了包的信息的傳播速度荸实。幸好服務(wù)發(fā)現(xiàn)數(shù)據(jù)量不是很大匀们,udp包也完全滿足。但只有一種情況除外准给,那就是新節(jié)點(diǎn)加入集群時(shí)需要同步自己全部的數(shù)據(jù)泄朴,此時(shí)數(shù)據(jù)量就可能比較大了。如果只用udp傳輸協(xié)議露氮,可能導(dǎo)致數(shù)據(jù)同步比較慢祖灰。memberlist為此新增了tcp同步的功能,我們可以利用tcp同步的接口一次傳輸大量數(shù)據(jù)畔规,加速數(shù)據(jù)收斂速度局扶。

參考

https://github.com/hashicorp/memberlist
https://github.com/docker/libnetwork
Gossip Protocol
SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叁扫,隨后出現(xiàn)的幾起案子三妈,更是在濱河造成了極大的恐慌,老刑警劉巖陌兑,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沈跨,死亡現(xiàn)場(chǎng)離奇詭異由捎,居然都是意外死亡兔综,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門狞玛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)软驰,“玉大人,你說(shuō)我怎么就攤上這事心肪《Э鳎” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵硬鞍,是天一觀的道長(zhǎng)慧瘤。 經(jīng)常有香客問我戴已,道長(zhǎng),這世上最難降的妖魔是什么锅减? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任糖儡,我火速辦了婚禮,結(jié)果婚禮上怔匣,老公的妹妹穿的比我還像新娘握联。我一直安慰自己,他們只是感情好每瞒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布金闽。 她就那樣靜靜地躺著,像睡著了一般剿骨。 火紅的嫁衣襯著肌膚如雪代芜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天懦砂,我揣著相機(jī)與錄音蜒犯,去河邊找鬼。 笑死荞膘,一個(gè)胖子當(dāng)著我的面吹牛罚随,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羽资,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼淘菩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了屠升?” 一聲冷哼從身側(cè)響起潮改,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腹暖,沒想到半個(gè)月后汇在,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脏答,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年糕殉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殖告。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阿蝶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黄绩,到底是詐尸還是另有隱情羡洁,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布爽丹,位于F島的核電站筑煮,受9級(jí)特大地震影響辛蚊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜真仲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一嚼隘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袒餐,春花似錦飞蛹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至焰宣,卻和暖如春霉囚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匕积。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工盈罐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闪唆。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓盅粪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親悄蕾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子票顾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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