《从Paxos到ZooKeeper 分布式一致性原理与实践》读书笔记
一、分布式架構
1、分布式特點
- 分布性
- 對等性。分布式系統中的所有計算機節點都是對等的
- 并發性。多個節點并發的操作一些共享的資源
- 缺乏全局時鐘。節點之間通過消息傳遞進行通信和協調,因為缺乏全局時鐘,很難定義兩個事件誰先誰后
- 故障總是會發生。系統設計時,需要考慮到任何異常情況
2、分布式環境的各種問題
- 通信異常。分布式系統中的某些節點之間無法正常通信
- 網絡分區。這有部分節點可以正常通信,有些無法正常通信。這種現象稱為網絡分區,也稱為“腦裂”
- 三態。節點之間的一次通信存在三種狀態:成功、失敗、超時
- 節點故障。節點機器宕機、失去回應
3、傳統事務的ACID理論。
- 原子性(Atomicity)。事務內的所有操作要么全部成功,要么全部失敗
- 一致性(Consistency)。事務的執行不能破壞數據庫的一致性。如果事務執行一半停了,一部分的修改寫入的數據庫。這時候,數據庫就處于一種不正確的狀態,或者說不一致的狀態
- 隔離性(Isolation)。多個事務并發執行,彼此不會受影響。事務的隔離級別:讀未提交(可能發生臟讀、重復讀、幻象讀)、讀已提交(肯能發生重復讀、幻象讀)、可重復讀(可能發生幻象讀)、串行化。隔離級別越高,對并發的性能影響越大,越能保證數據庫的一致性。
- 持久性(Durablility)。一旦事務成功提交,它對于數據的修改就被永久保存下來
4、分布式事務的CAP理論和BASE理論
- CAP理論。一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance),在分布式系統中,最多只能滿足其中的兩項。
- BASE理論。基本可用(Basically Available)、軟狀態(Soft state)、最終一致性(Eventually consistent)。基本可用指系統出現故障時,允許損失部分可用性,包括響應時間的損失和功能上的系統降級;軟狀態指允許節點間的通訊出現中間狀態;最終一致指系統的所有的數據副本,在一定時間的同步后,最終能夠達到一致的狀態
二、一致性協議
1、2PC。二階段提交協議
階段一,執行事務
- 事務詢問。協調者向所有參與者發送事務內容,等待參與者回應
- 執行事務。參與者執行事務,記錄Undo和Redo日志
- 反饋事務詢問響應,參與者返回給協調者Yes或No響應。全部返回Yes,進入提交事務階段;存在No返回或者超時,進入中斷事務階段
階段二,提交事務
- 發送事務提交請求
- 各個參與者提交事務
- 參與者反饋事務提交結果
- 如果參與者全部返回Yes,完成事務;存在返回No,進入中斷事務階段
中斷事務階段
- 發送回滾請求
- 事務回滾
- 反饋事務回滾結果
- 完成中斷事務
2PC的缺點
- 同步阻塞。各個參與者在等待其他參與者響應的同時,無法進行任何操作,處于阻塞狀態
- 單點問題。過度依賴協調者,一旦協調者出現問題,系統將無法正常運轉
- 數據不一致。同上一條,一旦協調者出現問題,就可能出現各個參與者數據不一致的問題
- 太過保守。一旦參與者出現故障,協調者只能通過自己的超時機制發現。
2、3PC。三階段提交協議
?階段一,CanCommit,事務詢問
- 事務詢問,詢問各個參與者能否完成事務
- 各個參與者返回響應,全部返回Yes,進入PreCommit階段
階段二,PreCommit,事務預提交
- 發送預提交請求
- 事務預提交。參與者執行事務操作,記錄Undo和Redo日志
- 參與者返回響應。全部返回Yes,繼續三階段;返回No,進入中斷事務階段
階段三,DoCommit,真正的事務提交
- 發送提交請求
- 參與者正式執行事務提交操作
- 返回協調者事務執行結果
- 協調者完成事務。如果存在返回No或者返回超時,進入中斷事務階段
中斷事務階段
- 發送回滾請求
- 事務回滾
- 反饋事務回滾結果
- 完成中斷事務
3PC優缺點
- 相較于2PC,3PC最大優點就是降低了參與者的阻塞范圍
- 缺點是,還是避免不了出現數據不一致的情況
3、Paxos算法
拜占庭將軍問題,拜占庭帝國的不同軍隊處于不同的地理問題,他們之間只能通過通訊員進行通訊,但是通訊員是不可靠的,可能篡改消息
由于加密算法和校驗算法的出現,所有實際的分布式系統之間的通訊,不存在數據被篡改的可能。
四、ZooKeeper和ZAB協議介紹
1、初識ZeeKeeper
ZooKeeper可以做什么
可以基于它實現負載均衡、集群管理、Master選舉、分布式隊列、分布式鎖、命名服務、數據發布/訂閱等功能
Zookeeper能保證的分布式一致性特性
- 順序一致性。同一個客戶端發起的請求,會嚴格按照發起順序執行
- 原子性。對于一個事務請求,集群中的所有機器的執行情況是一致的
- 可靠性。一旦服務端成功的應用了一個事務,并完成了對客戶端的響應。服務端的狀態會一直保存下來
- 實時性。保證在一段時間內的實時性
- 單一視圖。無論客戶端連接的是哪一個服務器,看到的服務端的數據模型都是一致的
Zookeeper的設計目標
- 簡單的數據模型。Zookeeper將去數據存儲在內存中,采用樹形結構存儲,樹由ZNode節點構成
- 可以構建集群。只有集群中超過一半機器能夠正常運作,整個集群就可以正常對外提供服務。
- 順序訪問。每個來自客戶端的請求,都會分配一個全局唯一的遞增編號
- 高性能。全量數據存儲在內存中,3臺3.4.3的Zookeeper集群,100%讀請求場景的壓測結果是12-13W的QPS
Zookeeper的基本概念
- 集群角色。存在Leader、Follower、Observer三種角色。Leader服務器提供讀和寫服務,Follower和Observer提供讀服務,但是Observer不參與選舉過程,也不參與寫操作的“過半寫”策略,因此,Observer可以在不影響寫性能的情況下提高讀性能
- 客戶端會話??蛻舳送ㄟ^TCP長連接和服務端相連,第一次建立連接就代表客戶單會話開始了??蛻舳四軌蛲ㄟ^心跳檢測與服務器保持有效的會話。Session的SessionTimeout值用來設置會話的超時時間,連接斷開后,只要在超時時間之內重新連接上了集群中的任何一臺服務器,那么之前創建的會話仍然有效
- 節點。一方面只集群中的每一臺機器,稱為機器節點;另一方面指數據模型中的數據單元,稱為數據節點Znode。Znode又分為持久節點和臨時節點,臨時節點的生命周期和會話綁定,一旦客戶端會話失效,那么這個客戶端創建的所有臨時節點都會被移除。
- 版本。每個Znode,會維護一個叫做Stat的數據結構,記錄了Znode的三個版本:version(當期版本)、cversion(當前Znode子節點的版本)、aversion(當前Znode的ACL版本)。
- Watcher。事件監聽器,允許用戶在一些節點上注冊一些watcher。特性事件觸發的時候,服務器會將事件通知到感興趣的客戶端上去。
- ACL。權限控制策略(Access Control Lists)。定義了五種權限:Create(創建子節點)、Delete(刪除子節點)、read(讀取節點數據和子節點列表)、write(更新節點數據)、admin(設置節點ACL的權限)
2、ZAB協議(Zookeeper Atomic Broadcast,原子消息廣播協議)
所有事務請求都由Leader服務器來處理分發,如果集群中的其他服務器收到了來自客戶端的請求,這些非Leader服務器會首先將這個事務請求轉發給Leader服務器,Leader負責將請求封裝成Proposal(提議)分發給集群中所有Follower,一旦收到超過半數的正確反饋,Leader就會再次向所有的Follower分發Commit消息,要求他們將前一個Prosocal提交
兩種基本模式:崩潰恢復模式和消息廣播模式
- 消息廣播模式。類似于二階段提交,去掉了中斷邏輯,當Leader服務器收到了超過半數的Follower的ACK響應后,就會廣播一個Commit消息給所有的Follower進行事務提交。
- 崩潰恢復。一旦Leader服務器出現崩潰,或者由于網絡原因失去了一半Follower服務器的聯系,就會進入崩潰恢復模式。
- 崩潰恢復需要確保已經被Leader提交的Proposal也能被所有Follower提交
- 確保丟棄只在Leader服務器上提出的事務。(此處的提出指的是二階段第一階段)。重新選舉出來的Leader擁有集群中所有服務器最高編號的事務Proposal
- 正常情況的數據同步:Leader服務器為每一個Follower服務器準備一個隊列,將那些沒有被Follower服務器同步的事務以Proposal消息的形式逐個發送給Follower,等待所有事務都同步到了Follower并成功應用到了Follower的本地數據庫中后,Leader服務器就將該Follower服務器加入到真正的可用列表中
- 事務編號ZXID,是一個64位的數字。前32位存儲Leader屆數,后32位記錄本屆Leader處理的消息數。
3、深入ZAB協議
運行分析,每一個進程都有可能處于以下三種狀態之一
- LOOKING:leader選舉階段
- FOLLOWING:Follower服務器和Leader服務器保持同步狀態
- LEADING:Leader服務器作為主進程領導狀態
五、使用Zookeeper
1、服務端部署與運行
- 初次使用,需要把/conf目錄下的zoo_sample.cfg文件重命名為zoo.cfg。配置如下:
- server.1=IP1:2888:3888,每一行這樣的配置代表一個集群中的一個機器,server.1中的1代表ServerID,同時在每臺機器上需要在數據目錄(dataDir指定的目錄)下創建一個myid文件,文件內容就是ServerID,id范圍是1~255
- 集群中每個機器的zoo.cfg文件都應該是相同的,最好使用git或者svn把配置管理起來
- 啟動服務。/bin/zhServer.sh? start
- 停止服務。/bin/zkServer.sh stop
2、客戶端腳本
- 啟動。/bin/zkCli.sh? -server ip:port(不加server參數,默認連接本機)
- 創建節點。create 【-s】【-e】path data acl。acl用來進行權限控制
- 讀取節點下子節點。ls path。例如:ls /? 查看根節點下的所有子節點
- 讀取節點數據。get path
- 更新節點數據。set path data 【version】
- 刪除節點。delete path 【version】。無法刪除一個包含子節點的節點
3、開源客戶端Curator的使用
直接看我的github代碼:https://github.com/leon66666/zookeeper-client
六、Zookeeper的典型應用場景
1、典型應用場景及實現
(1)數據發布和訂閱
分為推(push)模式和拉(pull)模式,push模式服務端主動把數據更新推送給所有訂閱的客戶端,而拉模式是由客戶端定時輪詢拉取的方式來獲取最新數據
應用場景:分布式系統統一配置,例如機器列表信息、運行時的開關配置、數據庫配置信息等,這些全局配置交給Zookeeper統一管理
這些配置具有以下特點:
- 數據量通常比較小
- 數據內容在運行時動態變化
- 集群中各機器共享,配置一致
(2)負載均衡
DDNS,動態DNS解析。局域網內部一般采用host綁定的方式來進行ip和域名的映射。一旦機器規模變大,這種做法就會相當的不方便。
通過Zookeeper實現,每個應用都可以創建一個屬于自己的數據節點作為域名配置的根節點,在這個節點上,每個應用都可以將自己的域名配置上去。通過注冊Watcher實現域名變更通知功能。
上圖為整體的DDNS系統架構。
- Register集群負責域名的動態注冊。每個服務者啟動的時候,都會把自己的域名信息注冊到Register集群中去。
- Dispatcher集群負責域名的解析。服務消費者在使用域名的時候,會向Dispatcher集群發出請求,獲取相應的IP:PORT信息。
- Scanner集群負責檢測及維護服務狀態(探測服務可用性、屏蔽異常服務節點)。(一種是心跳檢測,需要客戶端和服務端建立起tcp長連接。另一種是服務端主動進行狀態匯報,一旦超過5秒沒有收到匯報,就認為該IP地址不可用,進行域名清理)
- SDK,提供各個語言的系統接入
- Monitor負責收集集群信息以及對DDNS自身的監控
- Controller是一個后臺管理,負責授權管理、流量控制、服務配置和手動屏蔽服務等功能。
(3)命名服務
即分布式環境下,生成全局唯一ID的方法。大家一般會聯想到UUID,是通用唯一識別碼的簡稱。主流ORM框架HIbernate就有對UUID的直接支持。
但是,UUID有如下缺點:
- 長度過長,需要更多的存儲空間
- 含義不明,根據字符串開發人員從字面上根本看不出他的含義
利用Zookeeper來實現這類全局唯一ID的生成。當客戶端創建一個順序子節點的時候,zookeeper會自動以后綴的形式在其子節點上添加一個序號,利用了zookeeper順序節點的特性
(4)分布式協調/通知
Zookeeper實現分布式協調通知,通常的做法是不同的客戶端都對Zookeeper上同一個數據節點進行Watcher注冊。監聽節點數據的變化。
(5)通用的分布式系統機器間通信方法
分布西系統機器間通信包括:心跳檢測、工作進度報告、系統調度
- 心跳檢測。不同機器之間需要檢測到彼此是否正常運行。傳統方法通過機器之間能否互相ping通來判斷;更復雜的通過機器之間建立起Tcp長連接,通過tcp固有的心跳檢測機制來實現上層機器的心跳檢測;基于Zookeeper的臨時節點特性也可以實現心跳檢測,不同的機器在指定節點下創建臨時節點,不同機器可以通過判斷臨時節點是否存在來判斷客戶端機器是否存活。減少了系統耦合
- 工作進度報告。每個客戶端創建臨時子節點,各個任務機器會實時的將自己的執行進度存儲到對應的子節點上,也可以判斷子節點是否存在判斷機器是否存活
- 系統調度。一個分布式系統由控制臺和客戶端組成。后臺管理人員在控制臺做一些操作(實際上就是修改Zookeeper上某些節點的數據),Zookeeper以事件通知的形式發送給對應的訂閱客戶端
(6)集群管理
集群管理需求點
- 知道集群中工作的機器數量
- 對集群中每臺機器的運行狀態進行收集
- 對集群中的機器進行上下線操作
傳統的基于agent的管理方式,集群中每臺機器部署一個agent,負責本機器的監控和向中心系統匯報
- 大規模升級困難
- 統一的agent無法滿足多樣的需求。無法深入應用內部,對一些業務狀態進行監控
- 編程語言多樣性。不同機器需要提供不同語言的agent
利用zookeeper監控集群
- 客戶端可以對zookeeper節點進行監聽,節點變更會受到通知
- 在zookeeper上創建臨時節點,一旦會話失效,改臨時節點會被自動刪除
- 監控系統在/clusterServers節點上注冊一個Watcher監聽,添加機器會在監聽節點下創建臨時子節點。
zookeeper監控應用:分布式日志收集系統
- 注冊收集器機器。以/logs/controller作為收集器的根節點,每個收集器啟動的時候都會在收集器節點下創建自己的節點
- 任務分發。收集系統把日志源機器按照一定策略分配給注冊的收集器機器,將機器列表寫入到對應的收集器節點上
- 狀態匯報。收集器節點下面創建狀態子節點,每個注冊的收集器機器定時向該節點寫入自己的狀態信息和日志收集進度信息(可以看做是一種心跳檢測),根據更新時間來判斷是否存活
- 動態分配。檢測到收集器的減少或者增加之后,需要進行重新分配。通常有兩種做法:全局分配(影響面大);局部動態分配(低負載優先分配)
- 節點類型。收集器節點,需使用持久節點,需要保存該節點上的日志源機器列表
- 收集器節點監聽。放棄監聽設置,采用定期輪詢,節省網卡流量,但是具有一些延時,考慮到日志收集需求,延時是可以接受的
?(7)Master選舉
在集群的所有機器中選舉出一臺機器作為master
- 可以通過數據庫的主鍵唯一特性來實現。但是當選舉出的master掛了之后,數據庫無法通知我們這個事件
- 利用zookeeper的強一致性,客戶端無法創建一個已經存在的節點。其他沒有成功創建這個節點的客戶端會在這個節點上注冊一個子節點變更的watcher,一旦發現當前的master掛了,其余客戶端會重新進行選舉
(8)分布式鎖
最常見的是用數據庫實現。例如行鎖,表鎖,事務處理,樂觀鎖等等。但是往往分布式系統的性能瓶頸都集中在數據庫的操作上。
- 排它鎖。選擇一個節點作為鎖節點,客戶端加鎖的時候會在鎖節點先創建臨時子節點,利用zookeeper特性,只有一個客戶端能夠創建成功,其余客戶端注冊鎖節點的子節點變更的Watcher監聽,在持有鎖的客戶端主動刪除臨時節點或者由于宕機導致會話超時導致臨時節點被移除,都表示鎖被釋放了。其他監聽的客戶端會再次發起分布式鎖獲取
- 共享鎖。在鎖節點下創建臨時順序節點。讀節點為R+序號,寫節點為W+序號。創建完節點后,獲取所有子節點,對鎖節點注冊子節點變更的watcher監聽,確定自己的序號在所有子節點中的位置。對于讀請求,沒有比自己序號小的寫節點,就表示獲得了共享鎖,執行讀取邏輯。對于寫請求,如果自己不是序號最小的子節點,就需要進入等待。接收到watcher通知后,重復獲取鎖。
- 共享鎖羊群效應。大量的watcher通知和子節點列表獲取,兩個操作重復運行。集群規模比較大的情況下,會對zookeeper服務器造成巨大的性能影響和網絡沖擊
- 改進后的共享鎖。讀請求,監聽比自己小的寫節點。寫請求,監聽比自己小的最后一個節點。
- 具體選用哪種實現的共享鎖,視集群規模而定
(9)分布式隊列
- ?常規的先入先出隊列。通過臨時順序節點實現?!?】通過getChildren()接口獲取隊列節點下的所有子節點,如果自己不是序號最小的子節點,進入等待,向比自己序號小的最后一個節點注冊watcher監聽。收集通知后,重復步驟【2】
- Barrier模型。屏障節點的數據內容存儲n代表Barrier值。所有的客戶端都會在屏障節點下創建臨時節點,創建完畢獲取屏障節點內容,【2】獲取所有子節點,注冊對子節點列表變更的watcher監聽,統計子節點個數,不足barrier值,進入等待,收到watcher通知,重復步驟【2】
2、zookeeper在大型分布式系統中的應用
(1)hadoop。大型分布式計算框架
- 核心包括HDFS和MapReduce,分別提供了對海量數據的存儲和計算能力。0.23.0版本開始,Hadoop又引入了全新一代MapReduce框架YARN。
- YARN是為了提高計算節點Master的擴展性,引入的全新一代分布式調度框架。支持多個計算引擎,包括MapReduce、Spark、Storm等
- YARN中最核心的ResourceManager,作為全局的資源管理器,負責整個系統的資源管理和分配。存在單點問題,使用Active/Standby模式,解決單點問題。只有一臺處理Active狀態,其他處于Standby狀態,當Active節點掛掉之后,其余Standby節點會通過競爭選舉產生新的Active節點
- zookeeper實現Active/Standby模式。類似于master選舉,啟動的時候都會去創建一個臨時子節點,只要一個能夠創建成功,成功的機器作為Active,其他的作為Standby,并注冊子節點的watcher監聽。一旦Active掛點,會話斷開鏈接,臨時節點自動刪除,觸發watcher,再次進行master選舉出新的Active。
- HDFS的NameNode和ResourceManager模塊都是使用該組件實現的HA
- 腦裂問題。選舉出新的Active機器后,以前的Active恢復正常了,出現了腦裂現象。使用ACL權限控制來進行隔離,創建節點的同時增加修改這個節點的權限。當之前的Active機器恢復正常,嘗試去修改節點數據的時候,發現已經沒有了權限。
(2)HBase。是一個面向海量數據的高可靠性、高性能、面向列、可伸縮的分布式存儲系統
- 與大多數分布式NoSQL數據庫不同的是,HBase針對數據寫入具有強一致性、甚至包括索引列也都實現了強一致性
- HBase采用zookeeper服務來完成對整個系統的分布式協調工作
- 系統冗錯。每個RegionServer服務器都會信息節點,Hmaster對這個節點注冊監聽,當RegionServer掛掉之后,會話斷開,節點被刪除,Hmaster接收到刪除通知,會將掛掉的RegionServer所處理的數據分片(Region)重新路由到其他的節點上。隨著系統容量的不斷增加,Hmaster管理的負擔會越來越重,所以使用zookeeper來完成這部分工作,減輕Hmaster負擔。
- RootRegion管理。數據存儲的位置信息記錄在元數據分片(RootRegion)上。客戶端每次發起請求,需要知道數據的位置,就會去查詢RootRegion。而RootRegion的位置存儲在zookeeper上,當RootRegion發生變化或者發生故障時,就能夠通過zookeeper感知到這一變化做出一系列響應的容災措施。
- Region狀態管理。Region是HBase中數據的物理切片,每個Region中記錄了全局數據的一小部分,不同的Region之間數據不相互不重復的。對于一個分布式系統來說,Region會經常變更,變更原因來自于系統故障、負載均衡、配置修改、Region分裂和合并等。一旦Region發生移動,需要做上線和下線處理。狀態管理需要Zookeeper來實現。對于Hbase集群來說,Region的數量會達到10萬級別。
- 分布式SplitLog任務管理。
?(3)Kafka。開源的分布式消息系統,是一個吞吐量極高的分布式消息系統。主要用于實現低延遲的發送和收集大量的事件和日志數據。
- 每個broker服務器啟動的時候,都會向Zookeeper注冊。zookeeper作為注冊中心
- 使用zookeeper作為他的分布式協調框架,實現了生產者和消費者的負載均衡
(4)dubbo
七、Zookeeper技術內幕
1、系統模型
- 數據模型。由數據節點Znode組成樹型結構。每一個事務操作(節點的創建和刪除、數據節點內容變更、客戶端會話的創建和失效)zookeeper會分配一個全局唯一的事務ID,ZXID,64位數字。前32位表示leader選舉的屆數,后32位表示事務序號
- 節點特性。持久節點,持久順序節點,臨時節點,臨時順序節點。節點除了存儲數據,子節點之外,還存儲了節點本身的一些狀態信息,用Stat類來表示。
- 版本。版本信息也是存儲在stat中的。version(當前節點數據內容的版本號)、cversion(當前節點子節點的版本號)、aversion(當前節點ACL變更版本號)。通過版本號來實現樂觀鎖,會從客戶端請求中獲取到版本號,和節點狀態數據中存儲的版本號對比,如果不匹配,就拋出異常。
- Watcher,數據變更的通知??蛻舳俗詗atcher的時候,會將watcher對象存儲在客戶端的watchManager中,當服務端觸發watcher事件后,會向客戶端發送通知,客戶端線程從watchManager中取出對應的watcher對象來執行回調邏輯。watcher機制具有如下特點:一次性(無論是客戶端還是服務端,一旦一個watcher被觸發,zookeeper就會從相應的存儲中移除,所以需要反復注冊);客戶端串行執行(客戶端watcher的回調處理,是一個串行同步的過程,保證了順序性,也需要注意watcher回調方法的處理,避免長時間執行而影響到其他的watcher回調);輕量(只會告訴客戶端發生了事件,不會說明事件的具體內容,另外,客戶端注冊的時候,也不會把客戶端真實的watcher對象傳遞給服務器。如此輕量的設計,在網絡開銷和服務器內存開銷上都是非常廉價的)
- ACL,權限控制。分為權限模式(Scheme)、授權對象(ID)、權限(Permission),使用scheme:id:permission來識別一個有效的acl信息
- Scheme說明。IP模式:“IP:192.168.1.24”,“IP:192.168.1.1/24”;Digest模式:username:password,會進行兩次編碼處理,分別是SHA-1算法和BASE64編碼
- 授權對象(ID)。IP模式下是一個ip或一個ip段;Digest模式下,是username:password;World模型下,是“anyone”
- 權限(Permission)。create(C)創建子節點;delete(D)刪除子節點;read(R)讀取節點數據和子節點列表;write(W)更新節點數據;admin(A)節點管理權限,acl操作
2、序列化和協議
- jute介紹。是zookeeper的序列化組件,也是早期Hadoop中的默認序列化組件。后來由于Apache Avro具有出眾的跨語言特性、豐富的數據結構和對MapReduce的天生支持,并且能非常方便的用于RPC調用,所以后來的Hadoop就拋棄了Jute。但是zookeeper由于新老版本兼容、性能瓶頸并不在jute上、其他需求優先級更高等原因,一直使用著jute這個古老的序列化組件
- 通訊協議?;趖cp/ip協議,zookeeper實現了自己的通訊協議來完成客戶端與服務端、服務端與服務端之間的網絡通信。請求包括請求頭和請求體,響應包括響應頭和響應體
3、客戶端
?(1)一次會話的創建過程
- 初始化zookeeper對象
- 設置會話默認watcher
- 構造zookeeper服務器地址列表管理器:HostProvider
- 創建并初始化客戶端網絡連接器:ClientCncx。內部有兩個線程,SendThread(I/O線程,負責負責zookeeper客戶端和服務端之間的網絡I/O通信),ExentThread(事件線程,負責對服務器事件進行處理)。兩個核心隊列,outgoingQueue(客戶端請求發送隊列)、pendingQueue(服務端響應的等待隊列),還會創建底層I/O處理器ClientCxcnSocket
- 初始化和啟動SendThread,ExentThread
- 獲取一個服務器地址,通過HostProvider獲取
- 創建tcp長連接。ClientCxcnSocket負責創建一個tcp長連接
- SendThread負責構造一個ConnectRequest請求,放入到outgoingQueue請求隊列中
- ClientCxcnSocket負責從outgoingQueue隊列中取出一個待發送的對象,將其序列化成ByteBuffer后,發送給服務端
- ClientCxcnSocket接受服務器的響應,處理response,進行反序列化,
- 生成連接成功事件,交給ExentThread進行處理,從ClientWatcherManager查詢watcher,將其放入到EventThread的waitingEvents隊列中。
- 處理事件watcher,EventThread不斷的從waitingEvents隊列中取出待處理的watcher,調用watcher的process方法,執行回調邏輯。
(2)服務器地址列表
- 客戶端隔離命名空間
- HostProvider,會把所有的地址列表打散成一個環形隊列,不斷的從這個隊列中獲取地址
- 自定義HostProvider,實現動態變更的地址列表管理器和同機房優先策略
4、會話
(1)會話狀態。整個會話的運行期間,會在不同的狀態之間進行切換。這些狀態包括connecting,connected,reconnecting,reconnected,close。
- 客戶端由于網路原因斷開連接,會重連服務器。這個過程中狀態會在connecting和connected之間切換
- 會話超時、權限檢查失敗、客戶端主動退出程序。這些情況下,客戶端狀態直接變為close
(2)session是zookeeper的會話實體。包含以下屬性
- 會話ID。全局唯一的sessionid。sessionid計算算法:((System.currentTimeMillis()<<24)>>>8)|(id<<56)。當前系統時間左移24位再無符號右移8位,保證前8位都是0,用于來后邊的服務器唯一表示id<<56做位或運算。前8位表示服務器機器id,后56位表示時間毫秒。sessionid由服務器進行創建,基于順序執行的特定,所以所有客戶端的請求的時間毫秒都是不一樣的。這樣就保證了sessionid的全局唯一性。
- timeout。會話超時時間
- ticktime。下次會話超時時間點。用于分桶策略管理
- isclosing。標記一個會話是否已經關閉
(3)會話管理。由SessionTracker負責管理
- 分桶策略。按照ExpirationTime對會話進行分類,類似的會話放在同一區塊中進行管理統一處理。ExpirationTime=((currentTime+SessionTimeout)/ExpirationInterval+1)*ExpirationInterval。ExpirationInterval是leader服務器進行定期檢查會話超時的時間間隔,默認值是tickTime的值。ExpirationTime總是ExpirationInterval的整數倍。
- 會話激活。當客戶端向服務端發起請求的時候,會進行會話激活。重新計算會話的ExpirationTime。根據新舊兩個下次超時時間點,進行會話遷移,完成會話激活。有兩種情況會發生會話激活:1 是只要客戶端發送了請求,就會觸發一次會話激活;2 是如果客戶端在sessionTimeout/3時間內未和服務器進行過任何通信,就會主動向服務器發送ping請求(心跳檢測),觸發服務端的會話激活
- 會話超時檢查,按照ExpirationTime時間線,對會話桶進行檢查,留下的所有會話都是尚未被激活的,對他們進行批量清理
- 會話清理。標記會話狀態為關閉,向集群中所有機器發起會話關閉請求,收集需要刪除的臨時節點,刪除臨時節點,移除會話,關閉連接
(4)會話重連
- 連接斷開。connection_loss。客戶端會自動從服務器地址列表中重新逐個選取新的地址嘗試進行連接,直到最終成功連接上服務器。斷開連接和重連成功,客戶端都會受到服務端的事件通知
- 會話失效。session_expired。斷開連接之后,重連期間耗時過長,超過了會話超時時間,服務器認為這個會話已經結束,進行會話清理??蛻舳瞬恢酪呀浭?#xff0c;如果之后客戶端重新連接上了服務器,會受到會話已經失效的通知(session_expired)。
- 會話轉移。session_moved。斷開重新后,成功連接上了新的服務器,會話轉移到了新的服務器上。
5、集群版服務器啟動流程
(1)預啟動
- 統一由QuorumPeerMain作為啟動類
- 解析配置文件zoo.cfg
- 判斷當前是集群模式還是單機模式啟動(集群模式中,在zoo.cfg中配置了多個服務器地址)
(2)初始化
(3)leader選舉
(4)leader和follower啟動期交互過程
- 創建leader服務器,創建follower服務器
- follower服務器和leader建立連接,注冊follower
- leader服務器和follower服務器同步數據
- 過半follower服務器完成數據同步。
- 啟動leader服務器和follower服務器
?6、leader選舉
(1)選舉概述。服務器啟動時期的Leader選舉
- 每個server發出一個投票。初始化階段都會投給自己。投票以(myid,ZXID)表示。
- 接收來自各個服務器的投票。判斷投票的有效性、檢查是否是本輪投票、是否來自looking狀態的服務器
- 處理投票。把自己的投票和其他服務器的投票進行PK。PK規則:ZXID比較大的優先成為leader;ZXID相同,myid比較大的優先。把pk結果重新發給其他服務器
- 統計投票。每次投票后,都會統計是否有超過半數機器接收到相同的投票,大于等于n/2+1。
- 改變服務器狀態。leader變為leading,follower變為following
(2)選舉概述。服務器運行期間的leader選舉
- 變更狀態,當leader掛了之后,余下的非observer服務器都會講自己的狀態變為looking,開始進入選舉過程。
- 選舉過程同上。
(3)leader選舉的算法分析。3.4.0版本開始,只保留了tcp版本的FastLeaderElection。廢棄了LeaderElection和udp版本的FastLeaderElection
7、服務端請求處理
(1)會話創建請求
- 請求接收。I/O層接收請求,根據NIOServerCnxn是否初始化來判斷是否是會話創建請求,反序列化請求,檢查客戶端Zxid(客戶端Zxid需小于服務端Zxid),根據服務端的配置協商sessionTimeout
- 會話創建。為客戶端生成sessionID,向sessionTracker中注冊會話,會話激活,生成會話密碼(作為會話在不同服務器中間轉移的憑證)
- 預處理。采用責任鏈模式,創建請求事務頭、請求事務體、注冊與激活會話(為了處理非Leader服務器轉發過來的會話創建請求)
- 事務處理。Sync流程:Leader服務器和follower服務器記錄事務日志的過程
- 事務處理。Proposal流程:投票和統計投票過程。類似于二階段提交協議的preCommit。生成提議Proposal,廣播提議,收集投票
- 事務處理。Commit流程:超過半數提議通過,廣播commit消息
- 事務應用。將事務變更應用到內存數據庫中,對于會話創建需要特殊處理,會話的管理由sessionTracker負責,只需要再次向sessionTracker注冊即可
- 會話響應。統計服務端處理所花費的時間,創建響應,序列化響應,I/O層發送響應給客戶端
(2)setDate請求
- 預處理。接收請求,反序列化,會話檢查、ACL權限檢查、數據版本檢查、生成事務
- 事務處理。同上
- 事務應用。將事務變更應用到內存數據庫中
- 請求響應。同上
(3)事務請求轉發。所有非Leader服務器收到了客戶端發來的事務請求,都會將請求轉發到Leader服務器來處理
(4)getData等非事務請求的流程
- 預處理。接收請求,判斷是否為客戶端會話創建請求,交給PrepRequestProcessor處理器進行處理,會話檢查
- 非事務處理。反序列化請求,獲取節點數據,ACL權限檢查,注冊Watcher
- 請求響應。同上
8、數據與存儲
(1)內存數據
- ?DataTree。是內存數據存儲的核心,是一個樹的結構。底層是ConcurrentHashMap<String,DataNode> nodes。節點的路徑(path)作為key,節點的數據內容DataNode作為value
- DataNode。是數據存儲的最小單元。包括節點的數據內容data[]、acl列表、節點狀態(stat)、還記錄了父節點的引用和子節點列表
- ZKDatabase。是zookeeper的內存數據庫,負責管理zookeeper的所有會話、DataTree存儲、事務日志。
(2)事務日志
- 文件存儲。配置中的dataDir目錄,是用來存儲日志文件的。每個日志文件的大小都是64M,以事務ID(ZXID)作為后綴,高32位代表Leader周期,低32位則是真正的操作序列號
- 日志內容存儲格式。事務日志是二進制表示,無法直接看出信息。需使用事務日志格式化工具。org.apache.zookeeper.Server.LogFormatter。使用方法如下:Java LogFormatter 事務日志文件。需要注意的是,這是一個記錄事務操作的日志文件,因此里面沒有任務讀操作的日志記錄
- 日志寫入。1.當前日志文件剩余空間不足4k,會進行預分配。文件的不斷追加寫入會觸發底層磁盤I/O為文件開辟新的磁盤塊(磁盤seek),為了避免磁盤seek的頻率,提高磁盤I/O效率;2.事務序列化;3.寫入事務日志文件流;4.事務日志刷入磁盤
- 日志截斷。非leader機器上記錄的zxid比leader服務器還要大,Leader會發送TRUNC命令,進行日志截斷,刪除所有包含或大于peerLastZxid的事務日志文件
(3)snapshot,數據快照。用來記錄Zookeeper服務器某一個時刻的全量內存數據內容,并將其寫入到指定的磁盤文件中
- 文件存儲。 通過dataDir來配置,快照文件也是用事務ID(ZXID)的十六進制作為文件后綴,該后綴標記了本次數據快照開始時刻的服務器最新ZXID。和事務日志不同,快照日志沒有采用預分配機制。
- 存儲格式,和事務日志存儲格式相同,需要使用格式化工具查看內容。會將數據節點逐個依次輸出,這里輸出的僅僅是數據節點的元數據(stat),并沒有輸出每個節點的數據內容
- 數據快照過程:1 判斷是否需要進行數據快照(采用過半隨機策略,避免所有機器同時進行數據快照,影響性能。logcount>(snapcount/2+randroll) snapcount是配置文件配置的數值,randroll是一個隨機值);2 切換事務日志文件,計數清零;3 創建數據快照異步線程; 4 獲取全量信息和會話信息 ;5 生成快照文件名 6 數據序列化,寫入磁盤
(4)初始化。服務器啟動期間,會進行數據初始化過程,將磁盤上的數據加載到內存中
- 處理快照文件,獲取最新的100個快照文件
- 對快照文件進行解析和校驗。如果最新的快照文件未通過解析和校驗,會逐個往下進行解析校驗。
- 獲取最新的ZXID
- 處理事務日志,獲取所有大于上一步ZXID的事務日志。把事務日志應用到內存數據庫中
- 再次獲取最新的ZXID
- 校驗epoch(leader選舉屆數)
(5)數據同步
peerLastZxID:該learner服務器最后處理的Zxid
minComminttedLog :Leader服務器提議緩存隊列ComminttedLog中最小的Zxid
maxComminttedLog:Leader服務器提議緩存隊列ComminttedLog中最大的Zxid
- 直接差異化同步(DIFF同步)。peerLastZxID介于minComminttedLog和maxComminttedLog中間
- 先回滾在差異化同步(TRUNC+DIFF同步)。peerLastZxID介于minComminttedLog和maxComminttedLog中間,但是leader發現了某個learner包含了一條自己沒有的事務記錄。
- 僅回滾同步(RTUNC)。peerLastZxID大于maxComminttedLog
- 全量同步(SNAP)。peerLastZxID小于minComminttedLog;Leader沒有提議緩存隊列,peerLastZxID不等于Leader服務器最大的Zxid。在這兩種情況下,Leader服務器都無法直接使用提議緩存隊列和learner進行同步,因此只能使用全量同步(SNAP)
八、Zookeeper運維
1、配置詳解
(1)基本配置。必需配置
- ?clientPort:對外的服務端口,一般配置為2181、集群中的所有端口不需要保持一致
- dataDir:服務器存儲快照文件的目錄。dataLogDir,事務日志的存儲目錄
- tickTime:默認值3000毫秒,用于配置Zookeeper中最小時間單位的長度。會話的最小超時時間默認是2*tickTime
(2)高級配置
- dataLogDir:用于存儲事務日志文件。應該將事務日志單獨配置在一塊磁盤上,事務日志寫入的性能直接影響到zookeeper的性能和吞吐量。數據快照操作會極大的影響事務日志的寫性能,盡量分開磁盤
- initLimit:默認值為10,即tickTime的十倍。Leader等待follower完成數據同步的時間。隨著集群的數據量增大,同步時間變長,可以適當調大這個參數
- syncLimit:默認值5,leader服務器和follower進行心跳檢測的最大延時時間。如果網絡環境較差,可以適當調大此參數
- snapCount:默認值 100000。兩次快照之間間隔的事務操作次數
- preAllocSize:默認值65536,即64M。事務日志預分配的空間大小。隨著snapCount的變化而變化,同增同減
- minSessionTimeout。maxSessionTimeout:最大和最小超時時間。分別是tickTime的2倍和20倍。用于對客戶端回話超時時間進行限制
- maxClientCnxns:默認值60。如果設置為0,則表示沒有限制。單個客戶端和服務端之間的最大并發連接數。
- jute.maxbuffer:單個數據節點上存儲的最大數據量大小。zookeeper上不需要存儲太多的數據,往往還需要將該參數設置的更小
- clientPortAddress:針對多網卡的機器,zookeeper允許為每個ip地址指定不同的監聽端口
- server.id:host:port:port:配置組成集群的機器列表。第一個port用于leader和follower進行通信和數據同步的端口,第二個port用于leader選舉過程中的投票通信
- autopurge.snapRetainCount:自動清理快照日志和事務日志時,需要保留的數量。最小值為3,默認值也是3
- autopurge.purgeInterval:自動清理的頻率。默認值是0,單位是小時。配置為0或者負數,表示不開啟自動清理功能。默認不開啟此功能
- fsync.warningthresholdms:配置事務日志操作消耗時間的報警閾值,超過閾值,將在日志中打印出報警日志。
- forceSync:配置事務日志每次寫入操作強制寫入磁盤。默認值是yes。如果設置成no,在一定程度上能提高zookeeper的寫性能,但存在斷點的風險。
- globalOutStandingLimit:默認值1000,服務器最大請求堆積數量,防止服務器資源被大量的客戶端請求耗盡。
- leaderServers:是否允許leader服務器向客戶端提供服務。默認值yes??梢栽O置成no,讓leader服務器專注的進行分布式協調
- SkipAcl:默認值 no。配置是否跳過acl權限檢查。
- cnxTimeout:默認值5000毫秒。配置Leader選舉過程中,個服務器間tcp連接創建的超時時間
- eletionAlg:leader選舉策略。3.4.0版本之后,只留下了fastLeaderElection算法。
2、四字命令
- conf。輸出服務器的配置信息
- cons。輸出這臺服務器上所有客戶端連接的詳細信息
- crst。重置所有客戶端的連接統計信息
- dump。輸出當前集群中的所有會話信息。只有leader服務器會進行會話超時檢測,leader服務器執行此命令,還會打印出會話的超時時間
- envi。輸出當前服務器運行時的環境信息
- ruok。輸出當前服務器是否正在運行。
- stat。輸出當前服務器運行時的狀態信息,包括連接情況
- srvr。和stat命令功能相同,區別是srvr命令不會打印連接情況
- srst。重置所有服務器的統計信息
- wchs。輸出當前服務器上的watcher概要信息
- wchc。輸出當前服務器上管理的watcher詳細信息。以會話單元進行歸組
- wchp。輸出當前服務器上管理的watcher詳細信息。以節點路徑為單位進行歸組
- mntr。輸出比stat更詳細的服務器統計信息。
3、JMX。是一個為應用程序、設備、系統等植入管理功能的框架
(1)開啟遠程JMX
通過上述配置,就可以允許遠程機器和zookeeper服務器進行jmx連接了
(2)通過JConsole連接zookeeper
JConsole是一個Java內置的基于JMX的圖形化管理工具,是最常用的JXM連接器。
4、監控
TaoKeeper監控系統,可以在實時監控和數據統計兩方面保障Zookeeper的穩定性。github地址:https://github.com/alibaba/taokeeper
5、構建一個高可用的集群
(1)集群組成。最好的數量是奇數,不過偶數可以的
(2)容災。三機房部署,2機房部署
(3)擴容與縮容。Zookeeper集群擴容需要整個集群機器的重啟。整體重啟和逐臺重啟
九、相關資料
Zookeeper的優缺點
zookeeper有什么缺點?
總結
以上是生活随笔為你收集整理的《从Paxos到ZooKeeper 分布式一致性原理与实践》读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF Core Model更新迁移
- 下一篇: Node 10 新功能概览(译)