Zookeeper概述
- 2019 年 10 月 3 日
- 筆記
Zookeeper是什麼
ZooKeeper是一個開放源碼的分佈式協調服務,它是集群的管理者,監視着集群中各個節點的狀態根據節點提交的反饋進行下一步合理操作。最終,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
分佈式應用程序可以基於Zookeeper實現諸如數據發佈/訂閱、負載均衡、命名服務、分佈式協調/通知、集群管理、Leader選舉、分佈式鎖和分佈式隊列等功能。
Zookeeper保證了如下分佈式一致性特性:
- 順序一致性
- 原子性
- 單一視圖
- 可靠性
- 實時性(最終一致性)
客戶端的讀請求可以被集群中的任意一台機器處理,如果讀請求在節點上註冊了監聽器,這個監聽器也是由所連接的zookeeper機器來處理。對於寫請求,會統一由Leader接收並處理,廣播給其他zookeeper機器並且達成一致後,請求才會返回成功。因此,隨着zookeeper的集群機器增多,讀請求的吞吐會提高但是寫請求的吞吐會下降。
有序性是zookeeper中非常重要的一個特性,所有的更新都是全局有序的,每個更新都有一個唯一的時間戳,這個時間戳稱為zxid(Zookeeper Transaction Id)。而讀請求只會相對於更新有序,也就是讀請求的返回結果中會帶有這個zookeeper最新的zxid。
分佈式系統的CAP原則是什麼?ZK實現的是AP還是CP?
CAP原則又稱CAP定理,指的是在一個分佈式系統中,一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance)。CAP 原則指的是,這三個要素最多只能同時實現兩點,不可能三者兼顧。
一致性
我們知道ACID中事務的一致性是指事務的執行不能破壞數據庫數據的完整性和一致性,一個事務在執行前後,數據庫都必須處於一致性狀態。也就是說,事務的執行結果必須是使數據庫從一個一致性狀態轉變到另一個一致性狀態。
和ACID中的一致性不同,分佈式環境中的一致性是指數據在多個副本之間是否能夠保持一致的特性。
分佈式系統中,數據一般會存在不同節點的副本中,如果對第一個節點的數據成功進行了更新操作,而第二個節點上的數據卻沒有得到相應更新,這時候讀取第二個節點的數據依然是更新前的數據,即臟數據,這就是分佈式系統數據不一致的情況。
在分佈式系統中,如果能夠做到針對一個數據項的更新操作執行成功後,所有的用戶都能讀取到最新的值,那麼這樣的系統就被認為具有強一致性(或嚴格的一致性)。
可用性
可用性是指系統提供的服務必須一直處於可用的狀態,對於用戶的每一個操作請求總是能夠在有限的時間內返回結果,如果超過了這個時間範圍,那麼系統就被認為是不可用的。
“有限的時間內”是在系統的運行指標,不同系統會有差別。例如搜索引擎通常在0.5秒內需要給出用戶檢索結果。
“返回結果”是可用性的另一個重要指標,它要求系統完成對用戶請求的處理後,返回一個正常的響應結果,要明確的反映出對請求處理的成功或失敗。如果返回的結果是系統錯誤,比如”OutOfMemory”等報錯信息,則認為此時系統是不可用的。
分區容錯性
一個分佈式系統中,節點組成的網絡本來應該是連通的。然而可能因為某些故障,使得有些節點之間不連通了,整個網絡就分成了幾塊區域,而數據就散布在了這些不連通的區域中,這就叫分區。
當你一個數據項只在一個節點中保存,那麼分區出現後,和這個節點不連通的部分就訪問不到這個數據了。這時分區就是無法容忍的。
提高分區容忍性的辦法就是一個數據項複製到多個節點上,那麼出現分區之後,這一數據項仍然能在其他區中讀取,容忍性就提高了。然而,把數據複製到多個節點,就會帶來一致性的問題,就是多個節點上面的數據可能是不一致的。要保證一致,每次寫操作就都要等待全部節點寫成功,而這等待又會帶來可用性的問題。
總的來說就是,數據存在的節點越多,分區容忍性越高,但要複製更新的數據就越多,一致性就越難保證。為了保證一致性,更新所有節點數據所需要的時間就越長,可用性就會降低
在分佈式系統中,CAP不可能完全都保證,而由於系統硬件原因或系統之間的網絡原因肯定會導致網絡丟包,所以分區容錯性是一定要保證的,接下來就是要在C和A之間選擇了。
BASE理論
BASE理論是對CAP理論的延伸,思想是即使無法做到強一致性(CAP的一致性就是強一致性),但可以採用適當的採取弱一致性,即最終一致性。
Base 理論是對 CAP 中一致性和可用性權衡的結果,其來源於對大型互聯網分佈式實踐的總結,是基於 CAP 定理逐步演化而來的。其核心思想是:
既是無法做到強一致性(Strong consistency),但每個應用都可以根據自身的業務特點,採用適當的方式來使系統達到最終一致性(Eventual consistency)。
Basically Available(基本可用)
什麼是基本可用呢?假設系統,出現了不可預知的故障,但還是能用,相比較正常的系統而言:
-
響應時間上的損失:正常情況下的搜索引擎 0.5 秒即返回給用戶結果,而基本可用的搜索引擎可以在 1 秒作用返回結果。
-
功能上的損失:在一個電商網站上,正常情況下,用戶可以順利完成每一筆訂單,但是到了大促期間,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面。
Soft state(軟狀態)
什麼是軟狀態呢?相對於原子性而言,要求多個節點的數據副本都是一致的,這是一種 “硬狀態”。
軟狀態指的是:允許系統中的數據存在中間狀態,並認為該狀態不影響系統的整體可用性,即允許系統在多個不同節點的數據副本存在數據延時。
Eventually consistent(最終一致性)
這個比較好理解了哈。
上面說軟狀態,然後不可能一直是軟狀態,必須有個時間期限。在期限過後,應當保證所有副本保持數據一致性。從而達到數據的最終一致性。這個時間期限取決於網絡延時,系統負載,數據複製方案設計等等因素。
稍微官方一點的說法就是:
系統能夠保證在沒有其他新的更新操作的情況下,數據最終一定能夠達到一致的狀態,因此所有客戶端對系統的數據訪問最終都能夠獲取到最新的值。
常見數據一致性級別
常見的數據一致性級別有如下幾種:
(1)強一致性(strong consistency):任何時刻,任何用戶或節點都可以讀到最近一次成功更新的副本數據。強一致性是程度要求最高的一致性要求。
(2)單調一致性(monotonic consistency):任何時刻,任何用戶一旦讀到某個數據在某次更新後的值,這個用戶不會再讀到比這個值更舊的值。
(3)會話一致性(session consistency):任何用戶在某一次會話內一旦讀到某個數據在某次更新後的值,這個用戶在這次回話過程中不會讀到比這個值更舊的值。
(4)最終一致性(eventual consistency):一旦數據更新成功,各個副本上的數據最終達到完全一致的狀態,但達到完全一致狀態所需要的時間不能保障。對於最終一致性系統而言,一個用戶只要始終讀取某一個副本的數據,則可以實現類似單調一致性的效果,但用戶一旦更換讀取的副本,則無法保證任何一致性。
(5)弱一致性(week consistency):一旦某個更新成功,用戶無法再一個確定的時間內讀到這次更新的值,且及時在某個副本上讀到了新的值,也不能保證在其他副本上可以讀到新的值。
Zookeeper如何保證數據一致性?
什麼是ZAB協議?
-
Zab協議是為分佈式協調服務Zookeeper專門設計的一種支持 崩潰恢復 和 消息廣播 的 原子廣播協議 ,是Zookeeper保證數據一致性的核心算法。Zab借鑒了Paxos算法,但又不像Paxos那樣是一種通用的分佈式一致性算法。它是特別為Zookeeper設計的支持崩潰恢復的原子廣播協議。
-
在Zookeeper中主要依賴Zab協議來實現數據一致性,基於該協議,zk實現了一種主備模型(即Leader和Follower模型)的系統架構來保證集群中各個副本之間數據的一致性。 這裡的主備系統架構模型,就是指只有一台服務(Leader)負責處理外部的寫事務請求,然後Leader服務將數據同步到其他Follower節點服務。
Zookeeper 客戶端會隨機的鏈接到 zookeeper 集群中的一個節點,如果是讀請求,就直接從當前節點中讀取數據;如果是寫請求,那麼節點就會向 Leader 提交事務,Leader 接收到事務提交,會廣播該事務,只要超過半數節點寫入成功,該事務就會被提交。
Zab 協議的特性:
1)Zab 協議需要確保那些已經在 Leader 服務器上提交(Commit)的事務最終被所有的服務器提交。 2)Zab 協議需要確保丟棄那些只在 Leader 上被提出而沒有被提交的事務。
2pc兩階段提交
Zookeeper的leader選舉機制
- zxid:事務id,自增,64位,其中低32位才是zxid是自增的數字,高32位是選舉輪次,每次加一(低版本如此)。
- myid:服務器id,不可重複,在配置文件里配置的。LOOKING:服務器狀態,選舉狀態(處於選舉階段的狀態)。
- LEADERING:服務器狀態,領導狀態(leader)。
- FOLLOWER:服務器狀態,跟隨者。
- OBSERVER:服務器狀態,觀察者,跟隨者的一種但不參與選舉。
- 選票:選票包含myid和zxid,以(myid,zxid)的形式保存選票。
- 投票箱:每個服務器本地維護一個投票箱,用來存放自己的投票和其他節點發過來的投票。
- epoch輪次:投票的輪次。
- PK:選票PK,決定是否改票,算法的關鍵。
選舉輪次,低版本的領導者輪次在ZXID中的高32位,高版本的在選票信息里附加的。peerEpoch 提議的領導者的輪次
if (!backCompatibility) {//請求 > 28位元組 n.peerEpoch = response.buffer.getLong();//提議的領導者的輪次從選票信息獲取 } else {//請求 = 28位元組 if (LOG.isInfoEnabled()) {//向後兼容模式 LOG.info("Backward compatibility mode, server id=" + n.sid); } n.peerEpoch = ZxidUtils.getEpochFromZxid(n.zxid);//提議的領導者的輪次從ZXID的高32位獲取}
public class ZxidUtils { static public long getEpochFromZxid(long zxid) { return zxid >> 32L; }
//other code
}
- 若this.epoch<rece.epoch,說明自己已經落後,修改自己的輪次=收到的選票輪次,然後清空自己的投票箱再決定投給誰,然後把自己的投票廣播出去。
- 若this.epoch=rece.epoch,則比較zxid,自己的較大則不改票;若zxid相等則比較myid,若myid大則不改票,否則改為收到的選票;若this.zxid<rece.zxid,則改為收到的選票並廣播。
- this.epoch>rece.epoch,則忽略。
//提議的領導者的輪次從選票信息獲取