天天看點

《從Paxos到zookeeper:分布式一緻性原理與實踐》讀書筆記

Paxos,發音近似 帕克索斯。

問題的提出

并發的定義(來自《深入了解計算機系統》):

如果邏輯控制流在時間上重疊,那麼他們就是并發的。

本書的并發,指更新操作的并發,即有多個線程同時更新記憶體中變量的值。

資料複制的延時問題。資料一緻性指對一個副本資料進行更新的同時,必須確定也能夠更新其他的副本,否則不同副本之間的資料将不再一緻。

一緻性級别:

  • 強一緻性:理論上基本不存在。讀出的資料就是寫入的資料,這個時間間隔小到一定程度就不是一緻的;
  • 弱一緻性:分會話一緻性(對一個用戶端會話而言)和使用者一緻性(對一個使用者而言)
  • 最終一緻性:弱一緻性的特例。

第一章 分布式架構

集中式架構到分布式架構;

《分布式系統概念與設計》對分布式系統的定義:

硬體或者軟體元件釋出在不同的網絡計算機上,彼此之間僅僅通過消息傳遞進行通信和協調的系統。

分布式系統的特征:

  • 分布性
  • 對等性:資料副本
  • 并發性
  • 缺乏全局時鐘
  • 故障總是會發生

分布式系統的典型問題:

  • 通信異常
  • 網絡分區,即腦裂,叢集中隻有部分節點參與通信的狀态;
  • 三态:成功、失敗&逾時
  • 節點故障

從ACID到CAP/BASE

标準SQL規範定義四個事務隔離級别:未授權讀取、授權讀取、可重複讀取、串行化;

分布式事務:事務的參與者、支援事務的伺服器、資源伺服器、以及事務管理器分别位于分布式系統的不同節點上。

第二章 一緻性協定

  1. 二階段協定
  2. 三階段協定
  3. Paxos協定

二階段送出(Two-phaseCommit)是指,在計算機網絡以及資料庫領域内,為了使基于分布式系統架構下的所有節點在進行事務送出時保持原子性和一緻性而設計的一種算法。通常,二階段送出也被稱為是一種協定。

在分布式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的元件來統一掌控所有節點(稱作參與者)的操作結果并最終訓示這些節點是否要把操作結果進行真正的送出。二階段送出的算法思路可以概括為:參與者将操作成敗通知協調者,再由協調者根據所有參與者的回報情報決定各參與者是否要送出操作還是中止操作。

2.1協定說明:

階段一:送出事務請求

  1. 協調者節點向所有參與者節點詢問是否可以執行送出操作(vote),并開始等待各參與者節點的響應。
  2. 參與者節點執行詢問發起為止的所有事務操作,并将Undo資訊和Redo資訊寫入日志。
  3. 各參與者節點響應協調者節點發起的詢問。如果參與者節點的事務操作實際執行成功,則它傳回一個“YES”消息;如果參與者節點的事務操作實際執行失敗,則它傳回一個"NO”消息。

階段一也叫投票階段。

階段二:執行事務請求

協調者會根據各參與者的回報情況來決定最終是否可以進行事物送出操作,是以分為以下兩種情況:

執行事物送出

當協調者節點從所有參與者節點獲得的相應消息都為“YES”時:

  1. 協調者節點向所有參與者節點發出”正式送出(commit)”的請求。
  2. 參與者節點接受到COMMIT請求後正式完成操作,并釋放在整個事務期間内占用的資源。
  3. 參與者節點向協調者節點發送"ACK”消息。
  4. 協調者節點受到所有參與者節點回報的"ACK”消息後,完成事務。

中斷事物

如果任一參與者節點在第一階段傳回的響應消息為“NO”,或者 協調者節點在第一階段的詢問逾時之前無法擷取所有參與者節點的響應消息時,那麼就會中斷事物。

  1. 協調者節點向所有參與者節點發出”復原操作(rollback)”的請求。
  2. 參與者節點利用之前寫入的Undo資訊執行復原,并釋放在整個事務期間内占用的資源。
  3. 參與者節點向協調者節點發送"ACK”消息。
  4. 協調者節點受到所有參與者節點回報的"ACK”消息後,完成事務中斷。

不管最後結果如何,第二階段都會結束目前事務。

流程圖如下:

簡單來看:二階段送出将一個事物的處理過程分為了投票和執行階段,是一個先嘗試再送出的過程,是一個強一緻性的算法。

2.2優缺點:

優點:原理簡單,實作友善。

缺點:

  1. 同步阻塞問題。執行過程中,所有參與節點都是事務阻塞型的。當參與者占有公共資源時,其他第三方節點通路公共資源不得不處于阻塞狀态
  2. 單點故障。由于協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處于鎖定事務資源的狀态中,而無法繼續完成事務操作。(如果是協調者挂掉,可以重新選舉一個協調者,但是無法解決因為協調者當機導緻的參與者處于阻塞狀态的問題)
  3. 資料不一緻。在二階段送出的階段二中,當協調者向參與者發送commit請求之後,發生了局部網絡異常或者在發送commit請求過程中協調者發生了故障,這回導緻隻有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務送出。于是整個分布式系統便出現了資料部一緻性的現象。
  4. 太過保守:二階段送出缺乏較為完善的容錯機制,任意一個節點的失敗導緻整個事物的失敗。

三、三階段送出(3PC)

三階段送出(Three-phase commit),也叫三階段送出協定(Three-phase commit protocol),是二階段送出(2PC)的改進版本。

3.1協定說明

将二階段送出協定的“送出事務請求”一分為二,形成了CanCommit、PreCommit、DoCommit三個階段。示意圖如下:

3.1.1CanCommit階段

3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者如果可以送出就傳回Yes響應,否則傳回No響應。

1.事務詢問

協調者向參與者發送CanCommit請求。詢問是否可以執行事務送出操作。然後開始等待參與者的響應。

2.響應回報

參與者接到CanCommit請求之後,正常情況下,如果其自身認為可以順利執行事務,則傳回Yes響應,并進入預備狀态。否則回報No

3.1.2PreCommit階段

協調者根據參與者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能。

執行事務預送出

假如協調者從所有的參與者獲得的回報都是Yes響應,那麼就會執行事務的預執行。

1.發送預送出請求 協調者向參與者發送PreCommit請求,并進入Prepared階段。

2.事務預送出 參與者接收到PreCommit請求後,會執行事務操作,并将undo和redo資訊記錄到事務日志中。

3.響應回報 如果參與者成功的執行了事務操作,則傳回ACK響應,同時開始等待最終指令。

中斷事物

假如有任何一個參與者向協調者發送No響應,或者等待逾時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。

1.發送中斷請求 協調者向所有參與者發送abort請求。

2.中斷事務 參與者收到來自協調者的abort請求之後(或逾時之後,仍未收到協調者的請求),執行事務的中斷。

3.1.3docommit階段

該階段進行真正的事務送出,也可以分為以下兩種情況。

執行送出

  1. 發送送出請求 協調接收到參與者發送的ACK響應,那麼他将從預送出狀态進入到送出狀态。并向所有參與者發送doCommit請求。
  2. 事務送出 參與者接收到doCommit請求之後,執行正式的事務送出。并在完成事務送出之後釋放所有事務資源。
  3. 響應回報 事務送出完之後,向協調者發送Ack響應。
  4. 完成事務 協調者接收到所有參與者的ack響應之後,完成事務。

中斷事務

協調者沒有接收到參與者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能響應逾時),那麼就會執行中斷事務。

1.發送中斷請求 協調者向所有參與者發送abort請求

2.事務復原 參與者接收到abort請求之後,利用其在階段二記錄的undo資訊來執行事務的復原操作,并在完成復原之後釋放所有的事務資源。

3.回報結果 參與者完成事務復原之後,向協調者發送ACK消息

4.中斷事務 協調者接收到參與者回報的ACK消息之後,執行事務的中斷。

注意:

階段三,可能會出現:協調者出現問題,協調者和參與者網絡出現故障。無論哪種異常都會導緻參與者無法及時接收到來自協調者的doCommit或者rebort請求時,參與者會在等待逾時之後,會繼續進行事務的送出。

3.2優缺點:

優點:3PC主要解決的單點故障問題,并減少阻塞範圍,因為一旦參與者無法及時收到來自協調者的資訊之後,他會預設執行commit。而不會一直持有事務資源并處于阻塞狀态。

缺點:引入新問題,如果出現網絡分區,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待逾時之後執行了commit操作。這樣就和其他接到abort指令并執行復原的參與者之間存在資料不一緻的情況。

對于2PC、3PC無法徹底解決分布式一緻性問題後,就是paxos。

第三章 Paxos的工程實踐

第四章 zookeeper和paxos

zookeeper提供分布式協調服務,提供諸如統一命名服務、配置管理和分布式鎖、分布式消息等分布式的基礎服務。zookeeper的zab(zookeeper atomic broadCast),是paxos的一種改進。zookeeper是一個典型的分布式資料一緻性的解決方案,分布式應用可以基于zookeeper實作釋出\訂閱、負載均衡、命名服務、分布式協調\通知、叢集管理/master選舉、分布式鎖、和分布式隊列等功能。

zookeeper可以保證各種分布式一緻性問題:順序一緻性、原子性、單一視圖、可靠性、實時性。

zookeeper的設計目标: 提供簡單的資料模型、可以建構叢集、順序通路、高性能。

簡單的資料模型:zookeeper提供了一個共享的、樹型結構的命名空間來進行互相協調,這些資料結構都存在記憶體中,可以提高吞吐量。

建構分布式叢集:

順序通路:來自于client的事務,zookeeper都會配置設定一個全局唯一的遞增ID,這個ID反應了用戶端執行的順序。

高性能:zookeeper将全部資料結構存在記憶體中,并直接服務于用戶端的非事務請求。

zookeeper概念:

叢集角色

在分布式系統中,典型的應用是master/slave 主從模式的架構,提供全局寫操作的節點為master,通過異步複制的節點叫做slave。在zookeeper中,提出了leader、Follower、Observer。follower選舉産生leader,observer提供讀服務,是以zookeeper适用于讀多寫少的服務。

回話角色:

回話(session):當zookeeper用戶端啟動的時候,會與zookeeper建立一個長連接配接,zookeeper能夠通過向client發送心跳檢測client的狀态,通過zookeeper管理zookeeper的client(zookeeper的client為分布式應用的各個節點)。

資料節點:

在分布式應用中,一個是zookeeper管理的節點即為應用節點-機器節點,另一個節點類型為資料節點-znode,管理zookeeper資料模型的資料單元(zookeeper管理節點的資料)。zookeeper為樹型資料模型(Znode Tree) , 通過/分割的路徑就是一個ZNode,比如/foo/path1 , 為一個ZNode節點,儲存資料内容和屬性資訊。

版本:

zookeeper存放znode的版本。

watcher:

zookeeper允許使用者注冊事件,在某個時候觸發事件,配置設定給合适的client執行事件,watcher機制是zookeeper實作分布式協調服務的重要特征。

ACL機制:

zookeeper提供類似linux的權限控制機制。

第五章 使用zookeeper

部署:單機模式,僞分布模式和分布式叢集模式;

用戶端腳本,zkCli.sh,指令:

  • create:-s 順序節點,-e臨時節點,預設情況下不加參數是持久節點;
  • ls:隻能讀取指定節點的第一級的所有子節點;第一次部署的zk,預設在根節點下面有一個/zookeeper的保留節點;
  • get:
  • set:set指令後面有一個可選的version參數,用于指定本次更新操作是基于ZNode的哪一個資料版本進行的。
  • delete:不能有子節點存在,否則不能删除;

Java用戶端工具

zookeeper的用戶端和服務端會話建立是一個異步的過程;

create()方法有同步和異步兩種,都不支援遞歸建立節點,節點内容隻支援位元組數組byte[]類型,即不負責序列化工作,可以使用Hessian或者Kryo等專門的序列化工具;異步和同步的差別,異步方式下節點的建立過程(包括網絡通信和服務端節點建立過程)是異步的,在使用同步接口時需要注意可能抛出的異常,但是異步接口本身不會抛出異常,所有的異常都是在回調函數中通過ResultCode響應碼來展現。

delete,同樣有同步和異步之分;

getChildren,zk用戶端在擷取到指定節點的子節點清單之後,還需要訂閱這個子節點清單的變化通知,通過注冊watcher來實作。當有子節點被添加或者是删除時,服務端會向用戶端發送一個EventType.NodeChildrenChanged類型的事件通知(隻發送一次,watcher會失效,故用戶端需要反複注冊watcher),此時用戶端需要主動重新去擷取清單才能擷取到最新的清單。子節點清單都是資料節點的相對節點路徑;

getData,節點的資料内容或者是節點的資料版本變化,都是節點的變化;

setData,CAS:對于值V,每次更新前都會比對其值是否是預期值A,隻有符合預期,才會将V值原子化地更新到新值B。

exists;

權限控制,zk提供的幾種權限控制模式scheme:world、auth、digest、ip&super。當用戶端對一個資料節點添權重限資訊後,對于删除操作而言,其作用範圍是其子節點。

開源用戶端:ZkClient & Curator

ZkClient,提供Session逾時重連,watcher反複注冊;使用listener來實作watcher的反複注冊,注冊一次一直有效;deleteRecursive接口,提供遞歸删除子節點功能;

Curator

fluent風格,提供各種應用場景(共享鎖服務,master選舉,分布式電腦)的抽象封裝;重試政策ExponentialBackoffRetry;zk所有的非葉子節點必須是持久節點;BackgroundCallback接口,用來處理異步接口調用之後服務端傳回的結果資訊;CuratorEventType:CREATE、DELETE、EXISTS、GET_DATA/SET_DATA、CHILDREN、SYNC/GET_ACL/WATCHED、CLOSING等。響應碼,KeeperException.Code類,0(OK)、-4(ConnectionLoss)、 -100(NodeExists) 、 -112(SessionExpired);

典型使用場景

引入依賴curator-recipes;

  • 事件監聽,Curator提供Cache,對事件監聽的包裝,本地緩存視圖和遠端zookeeper視圖的對比過程,分為兩類,節點監聽NodeCache和子節點監聽PathChildrenCache,PathChildrenCacheEvent類定義所有的事件類型,包括新增子節點CHILD_ADDED、子節點資料變更CHILD_UPDATED、子節點删除CHILD_REMOVED。對于節點本身的變更,不能通知到用戶端。和其他zk用戶端工具一樣,Curator也不能對二級子節點進行事件監聽。
  • Master選舉,LeaderSelector.autoRequeue()和start()方法,構造函數傳入監聽器,LeaderSelectorListenerAdapter,LeaderSelectorListener.takeLeaderShip方法。
  • 分布式鎖,InterProcessMutex,提供方法acquire和release;
  • 分布式計數器,應用場景如統計網站的線上人數。思路:指定一個zk資料節點作為計數器,多個應用執行個體在分布式鎖的控制下,通過更新該資料節點的内容來實作計數功能。DistributedAtomicInteger類;
  • 分布式barrier,JDK自帶CyclicBarrier,用來控制多線程之間同步。DistributedBarrier提供setBarrier、waitOnBarrier、removeBarrier方法;除了主線程來出發Barrier釋放之外,還提供另一種線程自發觸發Barrier釋放的模式:enter和leave方法;
  • 工具類,ZkPaths,建構ZNode路徑,遞歸建立和删除節點;EnsurePath,一直能夠保證資料節點存在的機制,靜默的節點建立方式;TestingServer,在依賴包Curator-test包裡面,允許開發人員自定義zk伺服器對外服務的端口和dataDir路徑,不指定dataDir的情況下,預設在系統的臨時目錄java.io.tempdir下建立臨時目錄作為資料存儲目錄;TestingCluster,模拟叢集的工具,構造函數傳入模拟的叢集的節點數目;

第六章 zk的典型應用場景

主要包括:資料釋出/訂閱、負載均衡、命名服務、分布式協調/通知(注冊功能)、叢集管理、master選舉、分布式鎖、分布式消息隊列。

zk實作資料釋出/訂閱服務:

釋出/訂閱通常有兩種模式:推模式push/拉模式pull。推模式之後,服務端主動将資料更新發送給所有訂閱的用戶端;拉模式,用戶端主動發起請求去擷取最新資料,定時輪詢拉取。zookeeper是推拉結合的方式:用戶端向服務端注冊自己需要關注的節點,一旦該節點的資料發生變更,則服務端就會向相應的用戶端發送watcher事件通知,用戶端接收到消息通知之後,需要主動去服務端擷取最新資料。主要應用于分布式中,配置檔案的全局通知、更新。

全局配置資訊的3個特性:

資料量比較小;資料内容在運作時發生動态變化;叢集内各機器共享,配置一緻。

配置擷取:在機器啟動過程中,zookeeper的用戶端會擷取到其他用戶端配置檔案的改變。zk用戶端通過向zk注冊一個watcher監聽,監聽到用戶端配置檔案改變時,通知其他用戶端資訊改變。

配置更新:在系統運作過程中,當用戶端系統配置檔案改變時,zk通知其他注冊的用戶端,用戶端在收到變更通知後,更新擷取的資料。

zk的負載均衡:

負載均衡主要有硬體負載均衡和軟體負載均衡,通過負載均衡達到計算機叢集cpu、記憶體、網絡連接配接、I/O進行資源配置。zookeeper主要通過軟體進行負載均衡,zookeeper通過域名配置的功能,為IP配置相應的域名。

DNS系統可以看做一個超大規模的分布式映射表,域名和IP之間一一映射,向域名注冊服務商申請域名注冊,缺陷在于隻能注冊有限的域名;實際開發中,往往使用本地host檔案綁定來實作域名解析的工作,缺陷在于:當應用的機器規模擴大之後,需要到每個機器上去逐個進行變更,需要消耗大量時間,不能保證明時性。

域名配置:

域名解析:

域名變更:

問題在于:

更新版——自動化的DNS服務

域名注冊:

域名解析:

域名探測:

命名服務:

zk提供的命名服務功能和JNDI技術比較相似,通過一個資源引用的方式來實作對資源的定位和使用。分布式環境下,上層應用僅僅需要一個全局唯一的名字,通過這個唯一主鍵來定位資源。UUID的缺點:1. 長度過長;2. 含義不明。

分布式協調服務:

在分布式系統中,通過引入分布式系統協調者來控制整個分布式應用的運作,比如分布式任務的執行、分布式機器的協調工作。一般的做法是通過多個client在一個watcher上注冊(dubbo使用zk的注冊功能。),當client上資料發生變化時,所有watcher的訂閱者會收到相應的資料變化通知。

Master選舉:

分布式鎖:

zk通過分布式鎖控制用戶端對于共享資源的通路,當通路共享資源時,需要分布式鎖保證資源的一緻性。

分布式隊列:

第七章 zk技術内幕

第八章 zk運維

zk配置參數:

四字指令

使用有兩種方式:1. 使用telnet指令登入zk的對外服務端口,直接使用四字指令;2.使用nc指令:​​

​echo conf | nc localhost 2181​

​​。

指令概覽:

  1. conf,輸出zk伺服器運作時的基本配置資訊,如clientPort,dataDir,ticjTime;
  2. cons,輸出目前伺服器上所有的用戶端連接配接詳細資訊,client IP,Session ID,最近一次與伺服器互動的操作類型;
  3. crst,重置所有的用戶端連接配接統計資訊;
  4. dump,輸出目前叢集所有的會話資訊;
  5. envi,輸出zk所在伺服器的運作時環境資訊;
  6. ruok,輸出目前zk伺服器是否在運作,are you ok?正常則輸出imok,否則沒有任何響應。僅僅隻能表明目前伺服器是否在運作,确切地講,端口2181端口且四字指令執行流程正常,不能代表zk伺服器是否運作正常。不常用。也不是很有用。
  7. stat,輸出zk伺服器運作時狀态資訊;
  8. srvr,和stat類似,差別是僅僅輸出伺服器自身資訊;
  9. srst,重置所有服務端的統計資訊;
  10. schs,輸出目前伺服器管理的watcher的概要資訊;
  11. wchc,輸出目前伺服器管理的watcher的詳細資訊,以會話為機關進行歸組,同時列出被該會話注冊watcher的節點路徑;
  12. wchp,類似wchc,不同在于以節點路徑為機關進行歸組;
  13. mntr,輸出資訊比stat更詳細,伺服器的統計資訊,k-v鍵值對,可以進行監控;

JMX

四字指令僅僅用于監控,但實際上側重于監,不能用于控制;想要實作控制,需要JMX支援,zk3.3.0以上版本,使用标準JMX來對外提供運作時資料資訊和管控接口;zk預設開啟JMX功能。

找到zkServer.sh檔案,找到zoomain配置資訊,可以考慮修改authenticate=false(本地測試開發時)

​​

​ZOOMAIN=" -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain"​

第二種方式,通過JConsole連接配接zk

JConsole基于JMX,圖形化界面,需要多摸索摸索即可;TODO

Leader伺服器對外提供四個操作:

followerInfo:擷取所有follower的運作時資訊;

resetLatency:重置所有與用戶端請求處理延時相關的統計資訊;

resetMaxLatency:重置用戶端請求處理的最大延時統計;

resetStatistics:重置所有用戶端請求處理延時統計,以及所有用戶端資料包發送與接收的統計資訊。

InMemoryDataTree,zk伺服器的記憶體資料庫。

監控:實時監控與資料統計

建構高可用叢集

容災和擴容兩方面,5台機器與6台機器構成的zk叢集,容災能力沒有差别,基于此,zk叢集一般是奇數個節點。容災主要就是面對單點問題,單點的不僅僅是一個伺服器,有可能是一個機房。故而有三機房部署與兩機房部署方案(略);至于擴容和縮容,實際上就是zk叢集的重新開機問題,兩種方式:1. 全部重新開機,不推薦;2. 逐台重新開機;

日常運維

  1. 資料與日志管理,dataDir和dataLogDir兩個目錄分别用于存儲快照資料和事務日志。zk不會自己清理這兩個目錄,需要自己人工清理。三種清理方式:1. 純shell腳本,每天定時執行即可;2. 使用清理工具PurgeTxnLog,zk提供,該工具限制至少需要保留3個快照資料檔案。​

    ​java -cp zookeeper-3.4.5.jar :lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog /home/admin/taokeeper/zk_data /home/admin/taokeeper/zk_data -n 15​

    ​,至多保留15個檔案。3. 使用清理腳本zkCleanup.sh,基于PurgeTxnLog。自動清理機制,zk3.4.0版本之後,配置參數autoperge.snapRetainCount和autoperge.purgeInterval。
  2. Too many connections,maxCilentCnxns參數。
  3. 磁盤管理,挂載到單獨的磁盤。
#!/bin/bash
#snapshot file dir
dataDir=/home/nileader/taokeeper/zk_data/version-2
# tran log dir
dataLogDir=/home/nileader/taokeeper/zk_log/version-2
#zk log dir
logDir=/home/nileader/taokeeper/logs
# leave 60 files
count=60
count=$[$count+1]
ls -t $dataLogDir/log.* | tail -n +$count | xargs rm -f
ls -t $dataDir/snapshot.* | tail -n +$count | xargs rm -f
ls -t $logDir/zookeeper.log.* | tail -n +$count | xargs rm -f      

附錄