上一篇提到,項(xiàng)目用的分布式服務(wù)發(fā)現(xiàn)與注冊(cè)組件是consul,這篇文章主要來(lái)講下consul組件在項(xiàng)目中的應(yīng)用以及相關(guān)介紹。本文以官方文檔為主要參考consul文檔。
1. consul介紹
consul是一個(gè)服務(wù)管理軟件,主要功能如下:
- 支持多數(shù)據(jù)中心下,分布式高可用的,服務(wù)發(fā)現(xiàn)和配置共享。
- consul支持健康檢查,允許存儲(chǔ)鍵值對(duì)。
- 一致性協(xié)議采用
Raft算法,用來(lái)保證服務(wù)的高可用。 - 成員管理和消息廣播采用
GOSSIP協(xié)議,支持ACL訪問(wèn)控制。
1.1 服務(wù)注冊(cè)與發(fā)現(xiàn)
服務(wù)注冊(cè)是一個(gè)服務(wù)將其位置信息在“中心注冊(cè)節(jié)點(diǎn)”注冊(cè)的過(guò)程。該服務(wù)一般會(huì)將它的主機(jī)IP地址以及端口號(hào)進(jìn)行注冊(cè),有時(shí)也會(huì)有服務(wù)訪問(wèn)的認(rèn)證信息,使用協(xié)議,版本號(hào),以及關(guān)于環(huán)境的一些細(xì)節(jié)信息。
而服務(wù)發(fā)現(xiàn)可以讓一個(gè)應(yīng)用或者組件發(fā)現(xiàn)其運(yùn)行環(huán)境以及其它應(yīng)用或組件的信息。用戶配置一個(gè)服務(wù)發(fā)現(xiàn)工具就可以將實(shí)際容器跟運(yùn)行配置分離開(kāi)。常見(jiàn)配置信息包括:ip、端口號(hào)、名稱等。
在傳統(tǒng)情況下,當(dāng)出現(xiàn)服務(wù)存在于多個(gè)主機(jī)節(jié)點(diǎn)上時(shí),都會(huì)使用靜態(tài)配置的方法來(lái)實(shí)現(xiàn)服務(wù)信息的注冊(cè)。
而當(dāng)在一個(gè)復(fù)雜的系統(tǒng)里,需要較強(qiáng)的可擴(kuò)展性時(shí),服務(wù)被頻繁替換時(shí),為避免服務(wù)中斷,動(dòng)態(tài)的服務(wù)注冊(cè)和發(fā)現(xiàn)就很重要。
服務(wù)注冊(cè)與發(fā)現(xiàn)的組件有很多,如Zookeeper、Etcd等。既可用于服務(wù)間的協(xié)調(diào),同時(shí)又可用于服務(wù)的注冊(cè)。
1.2 Consensus Protocol - Raft
Consul使用Consensus協(xié)議Raft提供一致性(Consistency)。本文只是簡(jiǎn)單介紹在consul中的一致性,后面專門一篇寫raft。

首先,Raft是一種基于Paxos的Consensus算法。相比于Paxos,Raft設(shè)計(jì)采用了較少的狀態(tài),并且是一種更簡(jiǎn)單、更易于理解的算法。
只有Server節(jié)點(diǎn)參與Raft,且是peer set的一員。所有的Client節(jié)點(diǎn)只是轉(zhuǎn)發(fā)請(qǐng)求到Server。這種設(shè)計(jì)的考慮是,當(dāng)更多的成員加入到peer set中時(shí),quorum的規(guī)模也會(huì)增加??赡軙?huì)導(dǎo)致性能問(wèn)題是等待quorum個(gè)節(jié)點(diǎn)log entry。
啟動(dòng)Consul時(shí),單個(gè)consul節(jié)點(diǎn)需要以bootstrap模式運(yùn)行,該模式運(yùn)行自我選舉為leader。一旦Leader被選出來(lái),其他Server可以添加Peer set中,保持一致性和安全性。最終一些Server添加到集群,bootstrap模式需要禁用。
因?yàn)樗蠸erver都是Peer set中的成員,它們都知道誰(shuí)是Leader。當(dāng)一個(gè)RPC請(qǐng)求到達(dá)某個(gè)非Leader Server節(jié)點(diǎn),請(qǐng)求就會(huì)被轉(zhuǎn)發(fā)到Leader。如果RPC是一種query類型,這意味著它是只讀的,Leader會(huì)基于FSM當(dāng)前生成相應(yīng)的結(jié)果,如果RPC是一種transaction類型,即修改狀態(tài),Leader產(chǎn)生一個(gè)新的日志條目,并基于Raft算法進(jìn)行管理。一旦日志條目應(yīng)用于有限狀態(tài)機(jī),transaction完成。
由于Raft的replication性質(zhì),性能對(duì)網(wǎng)絡(luò)延遲是非常敏感的。為此,每個(gè)數(shù)據(jù)中心選擇獨(dú)立的Leader和維護(hù)一個(gè)不關(guān)聯(lián)的peer set。數(shù)據(jù)按照數(shù)據(jù)中心進(jìn)行劃分,所以每個(gè)Leader只負(fù)責(zé)在相應(yīng)數(shù)據(jù)中心的數(shù)據(jù)。當(dāng)接收到一個(gè)遠(yuǎn)程數(shù)據(jù)中心的請(qǐng)求時(shí),請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到相應(yīng)的Leader。這種設(shè)計(jì)在不犧牲一致性的情況實(shí)現(xiàn)較低延遲交易和更高的可用性。
雖然所有日志副本的寫入都是基于Raft,讀取更靈活。但為了支持開(kāi)發(fā)人員可能需要的各種權(quán)衡,Consul支持3種不同的一致性模式。
- Default,Raft采用Leader租賃模式,提供了一個(gè)時(shí)間窗口,在該時(shí)間段內(nèi),Leader角色是穩(wěn)定的。
- consistent,無(wú)條件一致性
- stale,這種模式允許在任何Server節(jié)點(diǎn)執(zhí)行讀取操作,無(wú)論它是不是Leader。
1.3 Group Membership Protocol - Gossip
Consul使用gossip協(xié)議管理成員關(guān)系、廣播消息到整個(gè)集群。詳情可參考Serf library。
Consul利用兩個(gè)不同的gossip pool。局域網(wǎng)(LAN Pool)和廣域網(wǎng)(WAN Pool)。
每個(gè)Consul數(shù)據(jù)中心都有一個(gè)包含所有成員(Server和Client)的LAN gossip pool。LAN Pool有如下幾個(gè)目的:
- 首先,成員關(guān)系允許Client自動(dòng)發(fā)現(xiàn)Server節(jié)點(diǎn),減少所需的配置量。
- 其次,分布式故障檢測(cè)允許的故障檢測(cè)的工作在某幾個(gè)Server幾點(diǎn)執(zhí)行,而不是集中整個(gè)集群所有節(jié)點(diǎn)上。
- 最后,gossip允許可靠和快速的事件廣播,如Leader選舉。
WAN Pool是全局唯一的,無(wú)論屬于哪一個(gè)數(shù)據(jù)中心,所有Server應(yīng)該加入到WAN Pool。由WAN Pool提供會(huì)員信息讓Server可節(jié)電執(zhí)行跨數(shù)據(jù)中心的請(qǐng)求。集成中故障檢測(cè)允許Consul妥善處理整個(gè)數(shù)據(jù)中心失去連接,或在遠(yuǎn)程數(shù)據(jù)中心只是單個(gè)的Server節(jié)點(diǎn)。
所有這些功能都是通過(guò)利用Serf提供。從用戶角度來(lái)看,它是作為一個(gè)嵌入式庫(kù)提供這些功能。但其被Consul屏蔽,用戶無(wú)需關(guān)心。作為開(kāi)發(fā)人員可以去了解這個(gè)庫(kù)是如何利用。
1.4 Session會(huì)話
上一篇文章snowflake升級(jí)版全局id生成中使用到了consul的KV存儲(chǔ)。
Consul提供session會(huì)話機(jī)制,可以用于構(gòu)建分布式鎖。session可以綁定到節(jié)點(diǎn)、健康檢查、KV數(shù)據(jù),目的是提供細(xì)粒度鎖。
KV存儲(chǔ)和會(huì)話的集成是使用會(huì)話的主要場(chǎng)景。必須在使用之前創(chuàng)建一個(gè)會(huì)話,然后使用它的ID。KV API支持acquire和release操作,acquire操作類似CAS操作,只有當(dāng)鎖空閑時(shí)才會(huì)返回成功。當(dāng)成功時(shí),某個(gè)normal標(biāo)識(shí)會(huì)更新,也會(huì)遞增LockIndex,當(dāng)然也會(huì)更新session的信息。
如果在acquire操作時(shí),與session相關(guān)的鎖已經(jīng)持有,那么LockIndex就不會(huì)遞增,但是key值會(huì)更新,這就允許鎖的當(dāng)前持有者無(wú)需重新獲得鎖就可以更新key的內(nèi)容。
一旦獲得鎖,所需要經(jīng)release操作來(lái)釋放(使用相同的session)。Release操作也類似于CAS操作。如果給定的session無(wú)效,那么請(qǐng)求會(huì)失敗。需要特別注意的是,無(wú)需經(jīng)過(guò)session的創(chuàng)建者,lock也是可以被釋放的。這種設(shè)計(jì)是允許操作者干預(yù)來(lái)終止會(huì)話,在需要的時(shí)候。如上所述,會(huì)話無(wú)效也將導(dǎo)致所有被持有的鎖被釋放或刪除。當(dāng)鎖被釋放時(shí),LockIndex不會(huì)變化,但是session會(huì)被清空,并且ModifyIndex遞增。這些語(yǔ)義允許元組(Key,LockIndex,Session)作為一個(gè)獨(dú)特的“序列”。這個(gè)序列可以被傳遞和用于驗(yàn)證請(qǐng)求是否屬于當(dāng)前的鎖持有者。因?yàn)槊看蝍cquire 都會(huì)導(dǎo)致LockIndex遞增,即使同一會(huì)話中重新獲取鎖,該序列能夠檢測(cè)到陳舊的請(qǐng)求。同樣,如果會(huì)話失效,相應(yīng)的LockIndex將為空。
要清楚的是,這種鎖系統(tǒng)是純粹的咨詢。并不是強(qiáng)制Client必須獲取鎖再能執(zhí)行操作作。任何客戶端都可以在未獲得鎖的情況下讀取、寫入和刪除Key操作。它不是Consul用于保護(hù)系統(tǒng)的方法。
2. consul架構(gòu)
上面介紹了consul的技術(shù)內(nèi)幕。現(xiàn)在來(lái)講講consul的架構(gòu)。

拆解開(kāi)這個(gè)體系,從每一個(gè)組件開(kāi)始了解。首先,可以看到有兩個(gè)數(shù)據(jù)中心,分別標(biāo)記為“one”和“two”。Consul是支持多數(shù)據(jù)中心一流,并且是常用業(yè)務(wù)場(chǎng)景。
每個(gè)數(shù)據(jù)中心都是由Server和client組成。建議有3~5臺(tái)Server,基于故障處理和性能的平衡之策。如果增加越多的機(jī)器,則Consensus會(huì)越來(lái)越慢。對(duì)client沒(méi)有限制,可以很容易地?cái)U(kuò)展到成千上萬(wàn)或數(shù)萬(wàn)。
同一個(gè)數(shù)據(jù)中心的所有節(jié)點(diǎn)都要加入Gossip協(xié)議。這意味著gossip pool包含給定數(shù)據(jù)中心的所有節(jié)點(diǎn)。有以下目的:首先,沒(méi)有必要為client配置服務(wù)器地址參數(shù);發(fā)現(xiàn)是自動(dòng)完成的。第二,節(jié)點(diǎn)故障檢測(cè)的工作不是放置在服務(wù)器上,而是分布式的。這使故障檢測(cè)比心跳機(jī)制更可擴(kuò)展性。第三,可用來(lái)作為消息層通知重要的事件,如leader選舉。
每個(gè)數(shù)據(jù)中心的服務(wù)器都是屬于一個(gè)Raft peer。這意味著,他們一起工作,選出一個(gè)的Leader,Leader server是有額外的職責(zé)。負(fù)責(zé)處理所有的查詢和事務(wù)。事務(wù)也必須通過(guò)Consensus協(xié)議復(fù)制到所有的伙伴。由于這一要求,當(dāng)非Leader Server接收到一個(gè)RPC請(qǐng)求,會(huì)轉(zhuǎn)發(fā)到集群的leader。
Server節(jié)點(diǎn)也是作為WAN gossip pool的一部分。這個(gè)pool是與LAN gossip pool是不同的,它為具有更高延遲的網(wǎng)絡(luò)響應(yīng)做了優(yōu)化,并且可能包括其他consul集群的server節(jié)點(diǎn)。設(shè)計(jì)WANpool的目的是讓數(shù)據(jù)中心能夠以low-touch的方式發(fā)現(xiàn)彼此。將一個(gè)新的數(shù)據(jù)中心加入現(xiàn)有的WAN Gossip是很容易的。因?yàn)槌刂械乃蠸erver都是可控制的,這也使跨數(shù)據(jù)中心的要求。當(dāng)一個(gè)Serfer接收到不同的數(shù)據(jù)中心的要求時(shí),它把這個(gè)請(qǐng)求轉(zhuǎn)發(fā)給相應(yīng)數(shù)據(jù)中心的任一Server。然后,接收到請(qǐng)求的Server可能會(huì)轉(zhuǎn)發(fā)給Leader。
多個(gè)數(shù)據(jù)中心之間是低耦合,但由于故障檢測(cè)、連接緩存復(fù)用、跨數(shù)據(jù)中心要求快速和可靠的響應(yīng)。
3. consul部署
3.1 docker安裝
docker安裝很簡(jiǎn)單,筆者這邊是基于docker-compose的配置文件,只需要本地安裝好docker和docker-compose,docker-compose.yml如下:
version: '3'
services:
consul:
image: consul
ports:
- "8500:8500"
- "8600:8600"
- "8300:8300"
拉取consul得最新image,進(jìn)行端口映射,暴露對(duì)外的端口8500,8300.
3.2 軟件安裝
- 從官網(wǎng)下載罪行的consul安裝包,https://www.consul.io/downloads.html。
- 解壓consul_0.6.4_darwin_amd64.zip。
- 將解壓后的二進(jìn)制文件consul拷貝到/usr/local/bin下。
- 寫配置文件。
服務(wù)注冊(cè)的配置文件如下:
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "1192.168.1.100",
"port": 8000,
"enableTagOverride": false,
"check": {
"id": "redis",
"name": "redis on port 8000",
"tcp": "localhost:8000",
"interval": "10s",
"timeout": "1s"
}
}
}
如上配置注冊(cè)了Redis的8000端口,并帶有tcp的health check。
節(jié)點(diǎn)的配置文件:
{
"datacenter": "east-cn",
"data_dir": "/opt/consul",
"log_level": "INFO",
"node_name": "redis",
"server": true,
"addresses": {
"https": "192.168.1.100"
},
"ports": {
"https": 0
},
"ui": true,
"retry-join": [
]
}
當(dāng)加載配置選項(xiàng)時(shí),consul是按照詞典順序從所有配置文件或目錄中加載。比如,a.json會(huì)先于e.json處理。后面設(shè)定的配置選項(xiàng)會(huì)合并到前面的配置集合中,如果存在重復(fù)的配置選項(xiàng)則會(huì)覆蓋。當(dāng)然,在某些情況下,比如事件處理程序,后面處理程序會(huì)追加到現(xiàn)有的配置選項(xiàng)中,形成事件處理程序列表。
3.3 啟動(dòng)
具體啟動(dòng)文檔見(jiàn)configuration。
如:
consul agent -server -config-dir /etc/consul.d -bind=192.168.1.100
-config-dir /etc/consul.d
config-dir
需要加載的配置文件目錄,consul將加載目錄下所有后綴為“.json”的文件,加載順序?yàn)樽帜疙樞颍募信渲眠x項(xiàng)合并方式如config-file。該參數(shù)可以多次配置。目錄中的子目錄是不會(huì)加載的。data-dir
此目錄是為Agent存放state數(shù)據(jù)的。是所有Agent需要的,該目錄應(yīng)該存放在持久存儲(chǔ)中(reboot不會(huì)丟失),對(duì)于server角色的Agent是很關(guān)鍵的,需要記錄集群狀態(tài)。并且該目錄是支持文件鎖。server
設(shè)置Agent是server模式還是client模式。Consul agent有兩種運(yùn)行模式:Server和Client。這里的Server和Client只是Consul集群層面的區(qū)分,與搭建在Cluster之上 的應(yīng)用服務(wù)無(wú)關(guān)。Consule Server模式agent節(jié)點(diǎn)用于采用raft算法維護(hù)Consul集群的狀態(tài),官方建議每個(gè)Consul Cluster至少有3個(gè)或以上的運(yùn)行在Server mode的Agent,Client節(jié)點(diǎn)不限。
其他常用的還有:
client
將綁定到client接口的地址,可以是HTTP、DNS、RPC服務(wù)器。默認(rèn)為“127.0.0.1”,只允許回路連接。RPC地址會(huì)被其他的consul命令使用,比如consul members,查詢agent列表node
節(jié)點(diǎn)在集群的名字,在集群中必須是唯一的。默認(rèn)為節(jié)點(diǎn)的Hostname。bootstrap
設(shè)置服務(wù)是否為“bootstrap”模式。如果數(shù)據(jù)中心只有1個(gè)server agent,那么需要設(shè)置該參數(shù)。從技術(shù)上來(lái)講,處于bootstrap模式的服務(wù)器是可以選擇自己作為Raft Leader的。在consul集群中,只有一個(gè)節(jié)點(diǎn)可以配置該參數(shù),如果有多個(gè)參數(shù)配置該參數(shù),那么難以保證一致性。bind
用于集群內(nèi)部通信的IP地址,與集群中其他節(jié)點(diǎn)互連可通。默認(rèn)為“0.0.0.0”,consul將使用第一個(gè)有效的私有IPv4地址。如果指定“[::]”,consul將使用第一個(gè)有效的公共IPv6地址。使用TCP和UDP通信。注意防火墻,避免無(wú)法通信。
3.4 結(jié)果
在開(kāi)啟了"ui": trueserver主機(jī)上,如http://192.168.1.100:8500/ui查看注冊(cè)中心的服務(wù)。
demo ui如下:

4. 總結(jié)
本文介紹了consul的一些內(nèi)幕及consul配置相關(guān),并對(duì)項(xiàng)目中的一些實(shí)際配置進(jìn)行展示。希望能夠幫助大家對(duì)consul相關(guān)的知識(shí)有所了解,并對(duì)于入門配置consul和實(shí)際應(yīng)用有所知道。個(gè)人認(rèn)為,consul原理還是簡(jiǎn)單易懂的,集群的配置也不復(fù)雜,安利大家使用。后面會(huì)再寫一篇介紹Spring cloud中集成和使用consul組件作為注冊(cè)與發(fā)現(xiàn)中心。
歡迎關(guān)注我的公眾號(hào)
