參考
老錢 服務(wù)發(fā)現(xiàn)的基本原理
服務(wù)發(fā)現(xiàn)
聊聊 Node.js RPC(二)— 服務(wù)發(fā)現(xiàn)
在傳統(tǒng)的系統(tǒng)部署中,服務(wù)運(yùn)行在一個(gè)固定的已知的 IP 和端口上绸贡,如果一個(gè)服務(wù)需要調(diào)用另外一個(gè)服務(wù)免猾,可以通過地址直接調(diào)用,但是吏夯,在虛擬化或容器話的環(huán)境中静浴,服務(wù)實(shí)例的啟動和銷毀是很頻繁的磷籍,服務(wù)地址在動態(tài)的變化适荣。服務(wù)發(fā)現(xiàn)的主要優(yōu)點(diǎn)是可以無需了解架構(gòu)的部署拓?fù)洵h(huán)境,只通過服務(wù)的名字就能夠使用服務(wù)院领,提供了一種服務(wù)發(fā)布與查找的協(xié)調(diào)機(jī)制弛矛。服務(wù)發(fā)現(xiàn)除了提供服務(wù)注冊、目錄和查找三大關(guān)鍵特性比然,還需要能夠提供健康監(jiān)控丈氓、多種查詢、實(shí)時(shí)更新和高可用性等强法。
一万俗、什么是服務(wù)發(fā)現(xiàn)?
服務(wù)發(fā)現(xiàn)并沒有怎樣的高深莫測饮怯,它的原理再簡單不過闰歪。只是市面上太多文章將服務(wù)發(fā)現(xiàn)的難度妖魔化,讀者被繞的云里霧里蓖墅,頓覺自己智商低下不敢高攀库倘。
- 服務(wù)提供者是什么临扮,簡單點(diǎn)說就是一個(gè)HTTP服務(wù)器,提供了API服務(wù)教翩,有一個(gè)IP端口作為服務(wù)地址杆勇。
- 服務(wù)消費(fèi)者是什么,它就是一個(gè)簡單的進(jìn)程饱亿,想要訪問服務(wù)提供者提供的服務(wù)來干一些事情蚜退。一個(gè)HTTP服務(wù)器既可以是服務(wù)提供者對外提供服務(wù),也可以是消費(fèi)者需要別的服務(wù)提供者提供的服務(wù)路捧,這就是服務(wù)依賴关霸,沒有你我就不是我自己传黄。復(fù)雜的服務(wù)甚至有多個(gè)服務(wù)依賴杰扫。
- 服務(wù)發(fā)現(xiàn)有三個(gè)角色,服務(wù)提供者膘掰、服務(wù)消費(fèi)者和服務(wù)中介章姓。服務(wù)中介是聯(lián)系服務(wù)提供者和服務(wù)消費(fèi)者的橋梁。服務(wù)提供者將自己提供的服務(wù)地址注冊到服務(wù)中介之宿,服務(wù)消費(fèi)者從服務(wù)中介那里查找自己想要的服務(wù)的地址郭脂,然后享受這個(gè)服務(wù)钾军。服務(wù)中介提供多個(gè)服務(wù),每個(gè)服務(wù)對應(yīng)多個(gè)服務(wù)提供者系忙。
服務(wù)中介就是一個(gè)字典,字典里有很多key/value鍵值對惠豺,key是服務(wù)名稱银还,value是服務(wù)提供者的地址列表。服務(wù)注冊就是調(diào)用字典的Put方法塞東西洁墙,服務(wù)查找就是調(diào)用字典的Get方法拿東西蛹疯。
當(dāng)服務(wù)提供者節(jié)點(diǎn)掛掉時(shí),要求服務(wù)能夠及時(shí)取消注冊热监,比便及時(shí)通知消費(fèi)者重新獲取服務(wù)地址捺弦。
當(dāng)服務(wù)提供者新加入時(shí),要求服務(wù)中介能及時(shí)告知服務(wù)消費(fèi)者孝扛,你要不要嘗試一下新的服務(wù)列吼。
二、Redis作為服務(wù)中介
Redis里面有豐富的數(shù)據(jù)結(jié)構(gòu)苦始,拿來存儲服務(wù)字典再合適不過了寞钥。對每一個(gè)服務(wù)名稱,我們用一個(gè)set結(jié)構(gòu)存儲服務(wù)的IP:Port字符串盈简。如果服務(wù)提供者加入凑耻,調(diào)用sadd命令加入服務(wù)地址太示,如果服務(wù)掛掉,調(diào)用srem命令移除服務(wù)地址香浩。對服務(wù)消費(fèi)者使用smembers指令獲取所有服務(wù)地址然后在消費(fèi)進(jìn)程里隨機(jī)挑一個(gè)类缤,或者使用srandmemember指令直接獲取隨機(jī)服務(wù)地址。
這個(gè)時(shí)候你也許會表示懷疑邻吭,服務(wù)發(fā)現(xiàn)真這么簡單么餐弱?答案是還差一點(diǎn),關(guān)于上面的這個(gè)解決方案有幾個(gè)問題囱晴。
1.第一個(gè)問題是服務(wù)提供者進(jìn)程如果被kill -9暴力殺死膏蚓,不能主動調(diào)用srem命令怎么辦?
這個(gè)時(shí)候服務(wù)列表中多了一個(gè)黑地址指向了不存在的服務(wù)而消費(fèi)者完全不知道畸写,這個(gè)時(shí)候服務(wù)中介就成了黑中介了驮瞧。那該怎么辦呢?
我們引入服務(wù)笨莘遥活和檢查機(jī)制论笔,并更換數(shù)據(jù)結(jié)構(gòu)。服務(wù)提供者需要每隔5秒左右向服務(wù)中介匯報(bào)存活千所,服務(wù)中介將服務(wù)地址和匯報(bào)時(shí)間記錄在zset數(shù)據(jù)結(jié)構(gòu)的value和score中狂魔。服務(wù)中介需要每隔10秒左右檢查zset數(shù)據(jù)結(jié)構(gòu),踢掉匯報(bào)時(shí)間嚴(yán)重落后的服務(wù)地址項(xiàng)淫痰。這樣就可以準(zhǔn)實(shí)時(shí)地保證服務(wù)列表中服務(wù)地址的有效性最楷。
2.第二個(gè)問題是服務(wù)列表變動時(shí)如何通知消費(fèi)者。有兩種解決方案待错。
第一種是輪詢籽孙,消費(fèi)者需要每隔幾秒查詢服務(wù)列表是否有改變。如果服務(wù)很多朗鸠,服務(wù)列表很大蚯撩,消費(fèi)者很多,redis會有一定壓力烛占。所以這時(shí)候可以引入服務(wù)列表的版本號機(jī)制胎挎,給每個(gè)服務(wù)提供一個(gè)key/value設(shè)置服務(wù)的版本號,就是在服務(wù)列表發(fā)生變動時(shí)忆家,遞增這個(gè)版本號犹菇。消費(fèi)者只需要輪詢這個(gè)版本號的變動即可知道服務(wù)列表是否發(fā)生了變化。因?yàn)榉?wù)列表比較穩(wěn)定芽卿,僅在網(wǎng)絡(luò)嚴(yán)重抖動的情況下才會頻繁發(fā)生變動揭芍,所以redis幾乎沒有壓力。
第二種是采用pubsub卸例。這種方式及時(shí)性要明顯好于輪詢称杨。缺點(diǎn)是每個(gè)pubsub都會占用消費(fèi)者一個(gè)線程和一個(gè)額外的redis連接肌毅。為了減少對線程和連接的浪費(fèi),我們使用單個(gè)pubsub廣播全局版本號的變動姑原。所謂全局版本號就是任意服務(wù)列表發(fā)生了變動悬而,這個(gè)版本號都會遞增。接收到版本變動的消費(fèi)者再去檢查各自的依賴服務(wù)列表的版本號是否發(fā)生了變動锭汛。這種全局版本號也可以用于第一種輪詢方案笨奠。
3.第三個(gè)問題是redis是單點(diǎn)的,如果掛掉了怎么辦唤殴?
這是個(gè)大問題般婆。正是因?yàn)檫@個(gè)問題的存在,流行的服務(wù)發(fā)現(xiàn)系統(tǒng)都是使用分布式數(shù)據(jù)庫zookeeper/etcd/consul等來作為服務(wù)中介朵逝,它們是分布式的多節(jié)點(diǎn)的蔚袍,掛掉了一個(gè)節(jié)點(diǎn)沒關(guān)系,系統(tǒng)仍然可以正常工作廉侧。
那如果整個(gè)zk集群掛掉會怎樣呢页响?其實(shí)每個(gè)服務(wù)消費(fèi)者在本地內(nèi)存里都會存一份當(dāng)前的服務(wù)列表篓足,即使服務(wù)中介集群掛掉段誊,也是可以使用當(dāng)前的服務(wù)列表正常工作的。
那redis作為服務(wù)中介就真的不靠譜了么栈拖?其實(shí)還有個(gè)redis-sentinel可以消除redis的單點(diǎn)問題连舍,redis-sentinel可以在主節(jié)點(diǎn)掛掉的時(shí)候,自動升級從節(jié)點(diǎn)為主節(jié)點(diǎn)涩哟。所以拿redis干這件事也是可以的索赏。用redis干服務(wù)發(fā)現(xiàn)確實(shí)非常簡單,雖然這種方式非常不流行贴彼。
三潜腻、服務(wù)提供者不只是HTTP服務(wù)
上面提到服務(wù)提供者簡單來說就是HTTP服務(wù)器,其實(shí)服務(wù)多種多樣器仗∪诨粒可以是數(shù)據(jù)庫服務(wù),可以是RPC服務(wù)精钮,可以是UDP服務(wù)等等威鹿。
如果是MySQL數(shù)據(jù)庫,那如何將MySQL服務(wù)注冊到服務(wù)中介呢轨香?原生的MySQL可沒有提供這樣功能忽你。一般做法是提供一個(gè)Agent代理去注冊。這個(gè)代理除了將服務(wù)地址注冊到服務(wù)中介外臂容,還需要監(jiān)控MySQL的健康狀況科雳,以便當(dāng)MySQL宕機(jī)時(shí)能及時(shí)切換到新的MySQL服務(wù)地址根蟹。一般這個(gè)Agent為了節(jié)省資源而不止監(jiān)控一個(gè)數(shù)據(jù)庫,它可以同時(shí)監(jiān)控多個(gè)數(shù)據(jù)庫糟秘,甚至是多種數(shù)據(jù)庫娜亿。
四、服務(wù)配置重加載
服務(wù)發(fā)現(xiàn)一般只是用來注冊和查找服務(wù)列表這樣一個(gè)比較單純的功能蚌堵。不過現(xiàn)代的服務(wù)發(fā)現(xiàn)系統(tǒng)還會集成服務(wù)配置管理功能买决。這樣可以實(shí)現(xiàn)服務(wù)配置的實(shí)時(shí)重加載。原理也很簡單吼畏,就是對于每一個(gè)服務(wù)項(xiàng)督赤,服務(wù)中介還會存儲一個(gè)單獨(dú)的key/value用來存儲這個(gè)服務(wù)的配置信息。當(dāng)這個(gè)配置項(xiàng)在后臺被修改時(shí)泻蚊,服務(wù)中介會實(shí)時(shí)通知相關(guān)服務(wù)器變更配置信息躲舌。比如數(shù)據(jù)庫地址變動,業(yè)務(wù)參數(shù)修改等性雄。
五没卸、服務(wù)管理后臺
為了便于服務(wù)管理,一般服務(wù)發(fā)現(xiàn)還會提供一個(gè)服務(wù)管理后臺秒旋,用于管理人員查看服務(wù)集群的狀態(tài)约计。如果服務(wù)注冊和匯報(bào)時(shí)提供冗余的配置信息,服務(wù)管理后臺就可以呈現(xiàn)更為詳細(xì)的服務(wù)信息迁筛。服務(wù)管理后臺還可以將所有的服務(wù)依賴組織起來煤蚌,呈現(xiàn)出一顆漂亮的服務(wù)依賴樹。
六细卧、兩種主要的服務(wù)發(fā)現(xiàn)方式
1.客戶端服務(wù)發(fā)現(xiàn)
在使用客戶端發(fā)現(xiàn)方式時(shí)尉桩,客戶端通過查詢服務(wù)注冊中心,獲取可用的服務(wù)的實(shí)際網(wǎng)絡(luò)地址(IP 和端口)贪庙。然后通過負(fù)載均衡算法來選擇一個(gè)可用的服務(wù)實(shí)例蜘犁,并將請求發(fā)送至該服務(wù)。優(yōu)點(diǎn):架構(gòu)簡單止邮,擴(kuò)展靈活这橙,方便實(shí)現(xiàn)負(fù)載均衡功能,缺點(diǎn):強(qiáng)耦合农尖,有一定開發(fā)成本析恋。
2.服務(wù)端服務(wù)發(fā)現(xiàn)
客戶端向load balancer 發(fā)送請求。load balancer 查詢服務(wù)注冊中心找到可用的服務(wù)盛卡,然后轉(zhuǎn)發(fā)請求到該服務(wù)上助隧。和客戶端發(fā)現(xiàn)一樣,服務(wù)都要到注冊中心進(jìn)行服務(wù)注冊和注銷。優(yōu)點(diǎn):服務(wù)的發(fā)現(xiàn)邏輯對客戶端是透明的并村。缺點(diǎn):需要額外部署和維護(hù)高可用的負(fù)載均衡器巍实。
3.服務(wù)發(fā)現(xiàn)和負(fù)載均衡的關(guān)系
最后提到負(fù)載均衡這個(gè)概念是為后面文章做鋪墊,因?yàn)樗苋菀缀头?wù)發(fā)現(xiàn)混淆哩牍,在很多簡單的場景我們甚至也不去刻意區(qū)分它們棚潦。但本質(zhì)上它們的層次和解決的問題是不一樣的,簡單說服務(wù)發(fā)現(xiàn)是負(fù)載均衡的前提膝昆,負(fù)載均衡要解決的是拿到服務(wù)列表后將流量合理的分配到各個(gè)節(jié)點(diǎn)上的問題丸边。
七、常見的服務(wù)發(fā)現(xiàn)框架
常見服務(wù)發(fā)現(xiàn)框架 Consul荚孵、 ZooKeeper以及Etcd
ZooKeeper 是這種類型的項(xiàng)目中歷史最悠久的之一妹窖,它起源于 Hadoop。它非常成熟收叶、可靠骄呼,被許多大公司(YouTube、eBay判没、雅虎等)使用蜓萄。
Etcd是一個(gè)采用 HTTP 協(xié)議的健/值對存儲系統(tǒng),它是一個(gè)分布式和功能層次配置系統(tǒng)澄峰,可用于構(gòu)建服務(wù)發(fā)現(xiàn)系統(tǒng)嫉沽。其很容易部署、安裝和使用摊阀,提供了可靠的數(shù)據(jù)持久化特性耻蛇。搭配一些第三方工具,etcd(健/值對存儲系統(tǒng))+ Registrator(服務(wù)注冊器) + Confd(輕量級的配置管理工具)
Consul 是強(qiáng)一致性的數(shù)據(jù)存儲胞此,使用 Gossip 形成動態(tài)集群。它提供分級鍵/值存儲方式跃捣,不僅可以存儲數(shù)據(jù)漱牵,而且可以用于注冊器件事各種任務(wù),從發(fā)送數(shù)據(jù)改變通知到運(yùn)行健康檢查和自定義命令