Zookeeper-docker版
1 zookeeper概述
1.1 什么是zookeeper
Zookeeper是一個分布式的、高性能的、開源的分布式系統的協調(Coordination)服務,它是一個為分布式應用提供一致性服務的軟件。
1.2 zookeeper應用場景
zookeeper是一個經典的分布式數據一致性解決方案,致力于為分布式應用提供一個高性能,高可用,且具有嚴格屬性訪問控制能力的分布式協調存儲服務。
1、維護配置信息
java編程經常會遇到配置項,比如數據庫的url,schema,user和password等。通常這些配置項我們會放置在配置文件中,在將配置文件放置在服務器上當需要更改配置的時,需要去服務器上修改對應的配置信息文件。但是隨著分布式系統的興起,由于許多服務都需要使用到該配置文件,必須保證該配置服務的高可用性和各臺服務器上配置數據的一致性。因此,通常會將配置文件部署在一個集群上,然而一個集群動輒上前臺服務器,此時如果在一臺一臺服務器逐個的修改配置文件將是非常繁瑣的一個操作。因此就需要一種服務,能夠高效快速且可靠的完成配置項的更新等操作,并能夠保證各個配置項在每一臺服務器上的數據一致性。
zookeeper就可以提供這樣一種服務,其使用Zab這種一致性協議來保證一致性。現在有很多開源項目使用zookeeper來維護配置,比如hhase中,客戶端就是連接一個zookeeper,獲得必要hbase集群的配置信息然后才可以進一步操作。還有開源的消息隊列kafka中,也是用zookeeper來維護broker的信息。
2、分布式鎖服務
一個集群是一個分布式系統,有多臺服務器組成。為了提高并發度和可靠性,多臺服務器運行著同一種服務。當多個服務在運行時就需要協調各服務的進度,有時候需要保證當某個服務在進行某個操作時,其他的服務都不能進行該操作,即對該操作進行加鎖,如果當前機器掛掉后,并釋放fail over到其他的機器繼續執行該服務。
3、集群管理
一個集群優勢會因為各種軟硬件故障或者網絡故障,出現某種服務器掛掉而被移除集群,而某些服務器加入到集群中的情況,zookeeper會將這些服務器加入/移出的情況下通知給集群匯總的其他正常工作的服務器,以及時調用存儲和計算等任務的分配和執行等。此外zookeeper還會對故障的服務器做出診斷并嘗試修復。
4、生成分布式唯一ID
在過去的單庫單表型系統中,通常可以使用數據庫字段自帶的auto_increment屬性來自動為每條記錄生成一個唯一的ID。但是分庫分表后,就無法再依靠數據庫的auto_increatment屬性來唯一標識一條記錄了,此時我們就可以用zookeeper在分布式環境下生成全局唯一ID。做法如下:每一個生成一個新ID時,創建一個持久順序節點,創建操作返回的節點序號,然后把比自己節點小的刪除即可。
1.3 zookeeper的設計目標
zookeeper致力于為分布式應用提供一個高性能,高可用,具有嚴格順序訪問控制能力的分布式協調服務。
1、高性能
zookeeper將全量數據存儲在內存中,并直接服務與客戶端的所有非事務請求,尤其適合用于以讀為主的應用場景。
2、高可用
zookeeper一般以集群的范式對外提供服務,一般3-5臺機器就可以組成一個可用的zookeeper集群,每一臺機器都會在內存中維護當前的服務器狀態,并且每臺機器之間都相互保持著通信。只要集群中超過一臺的機器都在工作,那么這個集群就能夠正常對外服務;
3、嚴格訪問數據
對于客戶端的每一個更新請求,Zookeeper都會分配一個全局唯一的遞增編號,這個編號反映了所有事務操作的先后順序。
2 Zookeeper的數據模型
2.1 zookeeper數據結構
Zookeeper數據模型的結構與Unix文件系統很類似,整體上可以看作是一顆樹,每一個節點稱做一個ZNode。每一個Znode默認能夠存儲1MB的數據,每個ZNode都可以通過其路徑唯一標識。
如何來描述一個ZNode呢?一個znode大體上分為3部分:
(1)節點的數據:即znode data(節點path,節點data的關系)就像是java map中(key,value)的關系。
(2)節點的子節點children
(2)節點的狀態stat:用來描述當前節點的創建,修改記錄,包括czxid、ctime等。
2.2 zookeeper節點類型
zookeeper中的節點有兩種類型,一種是臨時節點和永久節點。節點類型在創建是即被確定,并且不能改變。
(1)臨時節點:該節點的生命周期依賴于創建他們的會話。一旦會話(Session)結束,臨時節點將會被自動刪除,當然可以手動的進行刪除。雖然每個臨時的ZNode都會綁定到一個客戶端會話,但他們對所有的客戶端還是可見的。另外,Zookeeper的臨時節點不允許擁有子節點。
(2)持久化節點:該節點的生命周期不依賴于會話,并且只有在客戶點顯示執行刪除操作的時候,他們才能被刪除。
3 docker部署Zookeeper
3.1 部署準備
1、下載鏡像
[root@hadoop104 ~]# docker pull zookeeper:3.5.8
2、創建局域網
在集群部署在同一個局域網中。
[root@hadoop104 ~]# docker network create --subnet=192.168.10.0/24 zk_net
3、創建節點掛載目錄
(1)集群規劃,集群部署3臺機器:
集群編號ZOO_MY_ID | 名稱 | 映射本地端口 | ip | 存儲路徑 |
---|---|---|---|---|
1 | zk1 | 2181 | 192.168.10.101 | /usr/local zookeeper/zk1 |
2 | zk2 | 2182 | 192.168.10.102 | /usr/local zookeeper/zk2 |
3 | zk3 | 2183 | 192.168.10.103 | /usr/local zookeeper/zk3 |
(2)創建節點掛載目錄
[root@hadoop104 ~]# cd /usr/local
#創建 zookeeper節點配置存放目錄
[root@hadoop104 local]# mkdir -p zookeeper/zk1/conf zookeeper/zk2/conf zookeeper/zk3/conf
#創建 zookeeper節點數據存放目錄
[root@hadoop104 local]# mkdir -p zookeeper/zk1/data zookeeper/zk2/data zookeeper/zk3/data
#創建 zookeeper節點數據日志存放目錄
[root@hadoop104 local]# mkdir -p zookeeper/zk1/datalog zookeeper/zk2/datalog zookeeper/zk3/datalog
#創建 zookeeper節點日志存放目錄
[root@hadoop104 local]# mkdir -p zookeeper/zk1/logs zookeeper/zk2/logs zookeeper/zk3/logs
4、創建配置文件
(1)在第1個節點掛載目錄zookeeper/zk1/conf下分別創建配置文件zoo.cfg
[root@hadoop104 local]# vim zookeeper/zk1/conf/zoo.cfg
內容如下:
#Zookeeper保存數據的目錄,默認情況下,Zookeeper將寫數據的日志文件也保存在這個目錄里
dataDir=/data
#事務日志存儲地點,如果沒提供的話使用的則是 dataDir
dataLogDir=/datalog
#服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。tickTime以毫秒為單位
tickTime=2000
#集群中的follower服務器(F)與leader服務器(L)之間初始連接時能容忍的最多心跳數(tickTime的數量)
initLimit=5
#集群中的follower服務器與leader服務器之間請求和應答之間能容忍的最多心跳數(tickTime的數量)
syncLimit=2
#默認值為3,不支持以系統屬性方式配置。用于配置Zookeeper在自動清理的時候需要保留的快照數據文件數量和對應的事務日志文件。此參數的最小值為3,如果配置的值小于3會自動調整到3
autopurge.snapRetainCount=3
#默認值為0,單位為小時,不支持以系統屬性方式配置。用于配置Zookeeper進行歷史文件自動清理的頻率。如果配置為0或負數,表示不需要開啟定時清理功能
autopurge.purgeInterval=0
#默認為60,不支持以系統屬性方式配置。從Socket層面限制單個客戶端與單臺服務器之間的并發連接數,即以ip地址來進行連接數的限制。
#如果設置為0,表示不做任何限制。僅僅是單臺客戶端與單個Zookeeper服務器連接數的限制,不能控制所有客戶端的連接數總和
maxClientCnxns=60
#3.5.0中的新功能:當設置為false時,可以在復制模式下啟動單個服務器,單個參與者可以使用觀察者運行,并且群集可以重新配置為一個節點,并且從一個節點。
#對于向后兼容性,默認值為true。可以使用QuorumPeerConfig的setStandaloneEnabled方法或通過將“standaloneEnabled = false”或“standaloneEnabled = true”添加到服務器的配置文件來設置它。
standaloneEnabled=false
#內嵌的管理控制臺,停用這個服務
admin.enableServer=false
#開啟四字命令,將所有命令添加到白名單中
4lw.commands.whitelist=*
#集群中服務的列表,ip設置為局域網zk_net的網段
server.1=192.168.10.101:2888:3888;2181
server.2=192.168.10.102:2888:3888;2181
server.3=192.168.10.103:2888:3888;2181
zoo.cfg配置文件中參數的說明 解釋說明:
tickTime=2000 | zookeeper里面最小的時間單位為2000ms |
---|---|
initLimit=10 | Follower在啟動過程中,會從Leader同步所有最新數據,然后確定自己能夠對外服務的起始狀態。Leader允許F在 initLimit 時間內完成這個工作。通常情況下,我們不用太在意這個參數的設置。如果ZK集群的數據量確實很大了,F在啟動的時候,從Leader上同步數據的時間也會相應變長,因此在這種情況下,有必要適當調大這個參數了 |
syncLimit=5 | 在運行過程中,Leader負責與ZK集群中所有機器進行通信,例如通過一些心跳檢測機制,來檢測機器的存活狀態。如果L發出心跳包在syncLimit之后,還沒有從F那里收到響應,那么就認為這個F已經不在線了。注意:不要把這個參數設置得過大,否則可能會掩蓋一些問題 |
dataDir | 存儲快照文件snapshot的目錄。默認情況下,事務日志也會存儲在這里。建議同時配置參數dataLogDir, 事務日志的寫性能直接影響zk性能 |
dataLogDir | 事務日志輸出目錄。盡量給事務日志的輸出配置單獨的磁盤或是掛載點,這將極大的提升ZK性能 |
clientPort | 客戶端連接server的端口,即對外服務端口 ,默認是2181 |
server.1 | 配置集群節點 |
192.168.10.100:2888:3888 | 主機名, 心跳端口、數據端口 的格式 |
(2)分別創建第2,3個節點的配置文件
[root@hadoop104 local]# cp zookeeper/zk1/conf/zoo.cfg zookeeper/zk2/conf
[root@hadoop104 local]# cp zookeeper/zk1/conf/zoo.cfg zookeeper/zk3/conf
3.2 部署節點
(1)安裝并啟動第1個節點
[root@hadoop104 ~]# docker run -d --restart always --name zk1 --network zk_net --ip 192.168.10.101 -p 2181:2181 -e ZOO_MY_ID=1 -v /usr/local/zookeeper/zk1/data:/data -v /usr/local/zookeeper/zk1/datalog:/datalog -v /usr/local/zookeeper/zk1/logs:/logs -v /usr/local/zookeeper/zk1/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(2)安裝并啟動第2個節點
[root@hadoop104 ~]# docker run -d --restart always --name zk2 --network zk_net --ip 192.168.10.102 -p 2182:2181 -e ZOO_MY_ID=2 -v /usr/local/zookeeper/zk2/data:/data -v /usr/local/zookeeper/zk2/datalog:/datalog -v /usr/local/zookeeper/zk2/logs:/logs -v /usr/local/zookeeper/zk2/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(3)安裝并啟動第3個節點
[root@hadoop104 ~]# docker run -d --restart always --name zk3 --network zk_net --ip 192.168.10.103 -p 2183:2181 -e ZOO_MY_ID=3 -v /usr/local/zookeeper/zk3/data:/data -v /usr/local/zookeeper/zk3/datalog:/datalog -v /usr/local/zookeeper/zk3/logs:/logs -v /usr/local/zookeeper/zk3/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(4)查看節點狀態
集群中3個節點,只有一個是leader,其它節點都為flower。
#第1個節點狀態
[root@hadoop104 ~]# docker exec -it zk1 /bin/bash
root@245b7b533b30:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
#第2個節點狀態
[root@hadoop104 ~]# docker exec -it zk2 /bin/bash
root@36d3223cc495:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
#第2個節點狀態
[root@hadoop104 ~]# docker exec -it zk3 /bin/bash
root@450e6cf10d4f:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
3個節點狀態正常,集群搭建完成。
4 Zookeeper常用的Shell命令
4.1 基本操作
(1)連接ZooKeeper服務端:zkCli.sh –server ip:port
[root@hadoop104 ~]# docker exec -it zk1 /bin/bash
root@245b7b533b30:/apache-zookeeper-3.5.8-bin# zkCli.sh
[zk: localhost:2181(CONNECTED) 0]
(2)斷開連接:quit
(3)查看命令幫助:help
(4)顯示指定目錄下節點:ls 目錄
[zk: localhost:2181(CONNECTED) 7] ls /
[zookeeper]
4.2 新增節點
create [-s] [-e] path data # 其中 -s 為有序節點, -e 臨時節點
(1)創建持久化節點并寫入數據:
[zk: localhost:2181(CONNECTED) 0] create /test “123456”
Created /test
(2)創建持久化有序節,此時創建的節點名為指定節點名+自增序號
[zk: localhost:2181(CONNECTED) 2] create -s /test/sn “a”
Created /test/sn0000000001
[zk: localhost:2181(CONNECTED) 3] create -s /test/sn “b”
Created /test/sn0000000002
(3)創建臨時節點,臨時節點會在會話過期后被刪除
[zk: localhost:2181(CONNECTED) 4] create -e /tmp “tmp”
Created /tmp
(4)創建臨時有序節點,臨時節點會在會話過期后被刪除
[zk: localhost:2181(CONNECTED) 11] create -s -e /tmpsn “a”
Created /tmpsn0000000002
[zk: localhost:2181(CONNECTED) 12] create -s -e /tmpsn “b”
Created /tmpsn0000000003
4.3 獲取節點數據
get -s path 或 stat path
(1)獲取節點數據
[zk: localhost:2181(CONNECTED) 13] get /test
123456
(2)獲取子節點數據
[zk: localhost:2181(CONNECTED) 15] get /test/sn0000000001
a
(3)查看詳細信息
[zk: localhost:2181(CONNECTED) 20] get -s /test
123456
cZxid = 0x100000001
ctime = Thu Sep 12 07:33:53 UTC 2024
mZxid = 0x10000000b
mtime = Thu Sep 12 07:47:49 UTC 2024
pZxid = 0x100000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 2
節點各個屬性如下表。一個重要的概念是Zxid(ZooKeeper Transaction Id),ZooKeeper節點的每一個更改都具唯一的Zxid,如果Zxid1小于Zxid2,則Zxid1的更改發生在Zxid2更改之前。
狀態屬性 | 節點說明 |
---|---|
cZxid | 數據節點創建時的事務ID |
ctime | 數據節點創建世的時間 |
mZxid | 數據節點最后一個更新是的事務ID |
mtime | 數據節點最后一個跟新時的時間 |
pZxid | 數據節點的子節點最后一個被修改時的事務ID |
cversion | 子節點的更改次數 |
dataVerion | 節點數據的更改次數 |
aclVersion | 節點ACL的更改次數 |
ephemeralOwner | 如果節點是臨時節點,則表示創建該節點的會話的SeeesionID;如果是持久節點,則該屬性值為0 |
dataLength | 數據內容的長度 |
numChildren | 數據節點當前的子節點個數 |
4.4 更新節點
set path data [version]
(1)使用set命令來更新節點
[zk: localhost:2181(CONNECTED) 16] set /test “7890”
[zk: localhost:2181(CONNECTED) 17] get /test
7890
(2)根據版本號來更新節點
[zk: localhost:2181(CONNECTED) 23] get -s /test
abcd
cZxid = 0x100000002
ctime = Thu Sep 12 07:33:53 UTC 2024
mZxid = 0x10000000c
mtime = Thu Sep 12 07:54:30 UTC 2024
pZxid = 0x100000005
cversion = 3
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 3
[zk: localhost:2181(CONNECTED) 24] set /test “abcd” 3
[zk: localhost:2181(CONNECTED) 25] get /test
abcd
也可以基于版本號來進行更改,此時類似于樂觀鎖機制,當你傳入的數據版本號(dataVersion)和當前節點的數據版本號不符合時,zookeeper會拒絕本次修改:
4.5 刪除節點
delete path [version]
和更新節點數據一樣,也可以傳入版本號,當你傳入的數據版本號(dataVersion)和當前節點的數據版本號不符合時,zookeeper不會執行刪除操作。
(1)刪除節點
[zk: localhost:2181(CONNECTED) 27] delete /tmp
(2)要想刪除某個節點及其所有后代節點,可以使用遞歸刪除,命令為 rmr path 或 eleteall path。
[zk: localhost:2181(CONNECTED) 30] deleteall /test
4.6 監聽器
get -w path或 stat -w path
使用get path [watch] 注冊的監聽器能夠在節點內容發生改變的時候,向客戶點發出通知。需要注意的是zookeeper的觸發器是一次性的(One-time trigger),觸發一次后就會立即失效。
使用stat path [watch] 注冊的監聽器能夠在節點抓哪個臺發生改變的時候,向客戶點發出通知。
(1)監聽節點變化
客戶端窗口1
[zk: localhost:2181(CONNECTED) 32] create /watch “123456”
[zk: localhost:2181(CONNECTED) 35] stat -w /watch
另開一個客戶端窗口2
[root@hadoop104 ~]# docker exec -it zk2 /bin/bash
root@36d3223cc495:/apache-zookeeper-3.5.8-bin# zkCli
[zk: localhost:2181(CONNECTED) 0] set /watch “abcd”
在客戶端窗口1,輸出以下結果:
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/watch
5 zookeeper事件監聽機制
5.1 watcher概念
zookeeper提供了數據的發布/訂閱功能,對個訂閱者可同時監聽某一特定主題對象,當該主題對象的自身狀態發生變化時(例如節點內容改變,節點下的子節點列表改變等),會實時,主動通知所有訂閱者;
zookeeper采用了watcher機制實現數據的發布/訂閱功能。該機制在被訂閱對象發生變化時會異步通知客戶端,因此客戶端不必在Watcher注冊后輪詢阻塞,從而減輕了客戶點壓力。
watcher機制實際上與觀察者密室類似,也可以看作是一種觀察者密室在分布式場景下的實現方式。
5.2 wathcer架構
Watcher實現由三個部分組成:
- Zookeeper服務端
- Zookeeper客戶端
- 客戶端的ZKWatchManager對象
客戶端首先將Watcher注冊到服務端,同時將Watcher對象保存到客戶端的Watch管理器中。當Zookeeper服務端監聽的數據狀態發生變化時,服務端會主動通知客戶端,接著客戶端的Watch管理器會觸發相關Watcher來回調相應處理邏輯,從而完成整體的數據發布/訂閱流程。
5.3 wahcher特性
(1)一次性:wathcer是一次性的,一旦被觸發就會移除,再次使用時需要重新注冊。
(2)客戶端順序回調:watcher回調是順序串行化執行的,只有回調后客戶端才能看到最新的數據狀態。一個watcher回調邏輯不用太多,以免影響別的watcher執行。
(3)輕量級:wathcerEvent是最小的通信單元,結構上只包含通知狀態,事件類型和節點路徑,并不會告訴數據節點變化前后的具體內容。
(4)時效性:watcher只有在當前session徹底失效時才會無效,若session有效期內快速重連成功。則wacher依然存在,然后可接收到通知。
6 zookeeper應用場景
6.1 配置中心案例
工作中有這樣一個場景:數據庫用戶名和密碼信息放在一個配置文件中,應用讀取該配置文件,配置文件信息放入緩存。 若數據庫的用戶名和密碼改變時候,還需要重新加載緩存,比較麻煩,通過Zookeeper可以輕松完成,當數據庫發生變化時自動完成緩存同步。
設計思路:
(1)連接zookeeper服務器。
(2)讀取zookeeper中的配置信息,注冊watcher監聽器,存入本地變量。
(3)當zookeeper中的配置信息發生變化時,通過watcher的回調方法捕獲數據變化事件。
(4)重新獲取配置信息。
6.2 生成分布式唯一ID
在過去的單庫單表型系統中,通常可以使用數據庫字段自帶的auto_increment屬性來自動為每條記錄生成一個唯一的ID。但是分庫分表后,就無法再依靠數據庫的auto_increment屬性唯一標識一條記錄了。此時我們就可以用zookeeper在分布式環境下生成全局唯一ID。
設計思路:
(1)連接zookeeper服務器
(2)指定路徑下生成臨時有序節點
(3)取序號及為分布式環境下的唯一ID
6.3 分布式鎖
分布式鎖有多重實現方式,比如通過數據庫,redis都可以實現。作為分布式協同工具Zookeeper,當然也有著標準的實現方式。下面介紹在zookeeper中如何實現排它鎖。
設計思路:
(1)每個客戶端往/Locks下創建臨時有序節點/Locks/Lock_,創建成功后/Locks下面會有每個客戶端對應的節點。如/Locks/Lock_0000001
(2)客戶端取得/Locks下子節點,并進行排序,判斷排在最前面的是否為自己,如果自己的鎖節點在第一位,代表獲取鎖成功。
(3)如果自己的鎖節點不在第一位,則監聽自己前一位的鎖節點。如果自己鎖節點Lock_000002,那么則監聽Lock_0000001。
(4)當前一位鎖節點(Lock_000000001)對應的客戶端執行完成,釋放了鎖,將會觸發監聽客戶端(Lock_000002)的邏輯。
(5)監聽客戶端重新執行第2步邏輯,判斷自己是否獲得了鎖。
7 zookeeper的leader選舉
7.1一致性協議
zab協議的全稱是Zookeeper Atomic Broadcast (zookeeper原子廣播)。zookeeper是通過zab協議來保證分布式事務的最終一致性
基于zab協議,zookeeper集群中的角色主要有一下三類:
- 領導者(leader):領導者負責進行投票的發起和決議,更新系統狀態。
- 學習者(Learner):
- Follower:用于接受客戶請求并向客戶端返回結果,將寫請求轉發給leader節點,在選舉過程中參與投票。
- ObServer:用于接受客戶請求并向客戶端返回結果,但是ObServer不參加投票過程,只同步leader的狀態。ObServer的目的是為了擴展系統,提高讀取速度。
- 客戶端(Client) 請求發起方。
zab廣播模式工作原理,通過類似兩階段提交協議的方式解決數據一致性:
(1)leader從客戶端收到一個寫請求。
(2)leader生成一個新的事務并為這個事務生成一個唯一的ZXID。
(3)leader將這個事務提交(propose)發送給所有的follows節點。
(4)follower節點將收到的事務請求加入到歷史隊列(history queue)中,并發送ack給leader當leader收到大多數follower(半數以上節點)的ack消息,leader會發送commit請求。當follower收到commit請求時,從歷史隊列中將事務請求commit。
7.2 zookeeper的leader選舉
1、服務器狀態
(1)looking:尋找leader狀態。當服務器處于該狀態時,它會認為當前集群中沒有leader,因此需要進入leader選舉狀態。
(2)leading:領導者狀態。表明當前服務器角色是leader。
(3)followin:跟隨者狀態。表明當前服務器角色是follower。
(4)observing:觀察者狀態。表明當前服務器角色是observer。
2、服務器啟動時期的leader選舉
在集群初始化階段,當有一臺服務器server啟動時,其單獨無法進行完成leader選舉,當第二臺服務器server2啟動時,此時兩臺機器可以相互通信,每臺機器都試圖找到leader。于是進入leader選舉過程。選舉過程如下:
(1)每個server發出一個投票。由于是初始情況,server1和server2都會將自己作為leader服務器進行投票,投票會包含所推舉的服務器的myid(服務器的編號)和zxid(事務ID),使用(myid,zxid)來表示,此時server1的投票為(1,0),server2的投票為(2,0),然后各自將這個投票發給集群中其他機器。
(2)集群中的每一臺服務器接受來自集群中各個服務器的投票。
(3)處理投票。針對每一個投票,服務器都需要將別人的投票和自己的投票進行pk,pk規則如下:
①優先檢查zxid。zxid比較大的服務器優先為leader。
②如果zxid相同,那么就比較myid。myid比較大的服務器作為leader服務器。
對于Server1而言,它的投票是(1,0),接受Server2的投票為(2,0),首先會比較兩者的zxid,均為0,再比較myid,此時server2的myid最大,于是更新自己的投票為(2,0),然后重新投票,對于server2而言,其無須更新自己的投票,只是再次向集群中所有機器發出上一次的投票信息即可。
(4)統計投票。每次投票后,服務器都會統計投票信息,判斷是否已經有過半機器接受到相同的投票信息,對于server1、server2而言,都統計出集群中已經有兩臺機器接受了(2,0)的投票信息,此時便認為已經選出了leader。
(5)改變服務狀態。一旦確定了leader,每個服務器就會更新自己的狀態,如果是follower,那么久變更為following,如果是leader,就變更為leading。
2、服務器運行時期的Leader選舉
在zookeeper運行期間,leader與非leader服務器各司其職,即便當有非leader服務器宕機或新加入,此時也不會影響leader,但是一旦leader服務器掛了,那么這個集群將暫停對外服務,進入新一輪leader選舉,其過程和啟動時期的Leader選舉過程基本一致。
假設正在運行的server1、server2、server3三臺服務器,當前leader是server2,若某一時刻leader掛了、此時開始Leader選舉。選舉過程如下:
(1)變更狀態。leader掛后,剩余的服務器都會將自己的服務狀態變更為looking,然后開始進入leader選舉過程。
(2)每個server會發出一個投票。在運行期間,每個服務器上的zxid可能不同,此時假設server1的ZXID為122,server3的zxid為122,在第一輪投票中,server1和server3都會投自己、產生投票(1,122),(3,122),然后各自將投票發送給集群中的所有機器。
(3)接受來自各個服務器的投票。與啟動時過程相同。
(4)處理投票。與啟動時過程相同,此時,server3將會成為leader。
(5)統計投票。與啟動時過程相同。
(6)改變服務器的狀態。與啟動時過程相同。
7.3 observer角色及其配置
1、observer角色特點
(1)不參與集群的leader選舉
(2)不參與集群中寫數據時的ack反饋
2、配置observer
為了使用observer角色,在任何想變成observer角色的配置文件中加入如下配置:
peerType=observer
并在所有的server的配置文件中,配置成observer模式的server的哪行配置追加:observer。
例如:
server.3=192.168.10.103:2181:2183:observer