集羣任務html
主從架構java
在分佈式系統設計中一個獲得普遍應用的架構:一個主-從(master-worker)架構,該系統中遵循這個架構的一個重要例子是HBase——一個Google的數據存儲系統(BigTable)模型的實現,在最高層,主節點服務器(HMaster)負責跟蹤區域服務器(HRegionServer)是否可用,並分派區域到服務器。node
master-worker模式面臨的問題算法
若是主節點發送錯誤並失效,系統將沒法分配新的任務或從新分配已失敗的任務。這就須要重選備份主節點接管主要主節點的角色,進行故障轉移,數據恢復等等,更糟的是,若是一些從節點沒法與主要主節點通訊,如因爲網絡分區(network partition)錯誤致使,這些從節點可能會中止與主要主節點的通訊,而與第二個主要主節點創建主-從關係。針對這個場景中致使的問題,咱們通常稱之爲腦裂(split-brain):系統中兩個或者多個部分開始獨立工做,致使總體行爲不一致性。咱們須要找出一種方法來處理主節點失效的狀況,關鍵是咱們須要避免發生腦裂的狀況。數據庫
若是從節點崩潰,已分配的任務將沒法完成。若是從節點崩潰了,全部已派發給這個從節點且還沒有完成的任務須要從新派發。其中首要需求是讓主節點具備檢測從節點的崩潰的能力。主節點必須可以檢測到從節點的崩潰,並肯定哪些從節點是否有效以便派發崩潰節點的任務。一個從節點崩潰時,從節點也許執行了部分任務,也許所有執行完,但沒有報告結果。若是整個運算過程產生了其餘做用,咱們還有必要執行某些恢復過程來清除以前的狀態。安全
若是主節點和從節點之間沒法進行信息交換,從節點將沒法得知新任務分配給它。若是一個從節點與主節點的網絡鏈接斷開,好比網絡分區(network partition)致使,從新分配一個任務可能會致使兩個從節點執行相同的任務。若是一個任務容許屢次執行,咱們在進行任務再分配時能夠不用驗證第一個從節點是否完成了該任務。若是一個任務不容許,那麼咱們的應用須要適應多個從節點執行相同任務的可能性。bash
主從模式總結服務器
這是關鍵的一步,使得主節點能夠給從節點分配任務。網絡
主節點必須具備檢測從節點崩潰或失去鏈接的能力。session
主節點必須具備知道哪個從節點能夠執行任務的能力。
主節點和從節點必須具備經過某種可靠的方式來保存分配狀態和執行狀態的能力。
指望
理想的方式是,以上每個任務都須要經過原語(內核或微核提供核外調用的過程或函數稱爲原語(primitive))的方式暴露給應用,對開發者徹底隱藏實現細節。ZooKeeper提供了實現這些原語的關鍵機制,所以,開發者能夠經過這些實現一個最適合他們需求、更加關注應用邏輯的分佈式應用。
什麼是zookeeper
來源
Zookeeper 最先起源於雅虎研究院的一個研究小組。在當時,研究人員發現,在雅虎內部不少大型系統基本都須要依賴一個相似的系統來進行分佈式協調,可是這些系統每每都存在分佈式單點問題。因此,雅虎的開發人員就試圖開發一個通用的無單點問題的分佈式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。
zookeeper是什麼
ZooKeeper是一種用於分佈式應用程序的高性能協調服務.
ZooKeeper is a high-performance coordination service for distributed applications. It exposes common services - such as naming, configuration management, synchronization, and group services - in a simple interface so you don't have to write them from scratch. You can use it off-the-shelf to implement consensus, group management, leader election, and presence protocols. And you can build on it for your own, specific needs.
ZooKeeper是一個典型的分佈式數據一致性解決方案,其設計目標是將那些複雜且容易出錯的分佈式一致性服務封裝起來,構成一個高效可靠的原語集,並以一系列簡單易用的接口提供給用戶使用。分佈式應用程序能夠基於 ZooKeeper 實現諸如數據發佈/訂閱、負載均衡、命名服務、分佈式協調/通知、集羣管理、Master 選舉、分佈式鎖和分佈式隊列等功能。
初識
zk架構
角色
Leader
Leader做爲整個ZooKeeper集羣的主節點,負責響應全部對ZooKeeper狀態變動的請求。它會將每一個狀態更新請求進行排序和編號,以便保證整個集羣內部消息處理的FIFO。
Follower
Follower主要是響應本服務器上的讀請求外,另外follower還要處理leader的提議,並在leader提交該提議時在本地也進行提交。另外須要注意的是,leader和follower構成ZooKeeper集羣的法定人數,也就是說,只有他們才參與新leader的選舉、響應leader的提議。
Observe
爲客戶端提供讀服務器,若是是寫服務則轉發給Leader。不參與選舉過程當中的投票,也不參與「過半寫成功」策略。在不影響寫性能的狀況下提高集羣的讀性能。
client
鏈接zookeeper服務器的使用着,請求的發起者。獨立於zookeeper服務器集羣以外的角色。
數據模型znode
ZAB協議
特色
運用場景
Standalone模式演示開始,本地啓動
配置
ZooKeeper 中使用的基本時間單元, 以毫秒爲單位, 默認值是 2000。它用來調節心跳和超時。
默認值是 10, 即 tickTime 屬性值的 10 倍。它用於配置容許 followers 鏈接並同步到 leader 的最大時間。若是 ZooKeeper 管理的數據量很大的話能夠增長這個值。
默認值是 5, 即 tickTime 屬性值的 5 倍。它用於配置leader 和 followers 間進行心跳檢測的最大延遲時間。若是在設置的時間內 followers 沒法與 leader 進行通訊, 那麼 followers 將會被丟棄。
ZooKeeper 用來存儲內存數據庫快照的目錄, 而且除非指定其它目錄, 不然數據庫更新的事務日誌也將會存儲在該目錄下。
服務器監聽客戶端鏈接的端口, 也即客戶端嘗試鏈接的端口, 默認值是 2181。
/bin/命令
監控命令
在客戶端能夠經過 telnet 或 nc 向 ZooKeeper 提交相應的服務信息查詢命令。使用方式
echo mntr | nc localhost 2181
.
複製模式配置
zookeeper集羣模式下還要配置一個myid文件,這個文件須要放在dataDir目錄下,文件中寫入一個id便可。
server.1= 192.168.1.9:2888:3888
server.2= 192.168.1.124:2888:3888
server.3= 192.168.1.231:2888:3888
複製代碼
其中,id 被稱爲 Server ID,用來標識該機器在集羣中的機器序號(在每臺機器的 dataDir 目錄下建立 myid 文件,文件內容即爲該機器對應的 Server ID 數字)。host 爲機器 IP,port1 用於指定 Follower 服務器與 Leader 服務器進行通訊和數據同步的端口,port2 用於進行 Leader 選舉過程當中的投票通訊。
核心概念
數據模型znode
存儲
Zookeeper的數據模型是樹結構,在內存數據庫中,存儲了整棵樹的內容,包括全部的節點路徑、節點數據、ACL信息,Zookeeper會定時將這個數據存儲到磁盤上。
DataTree是內存數據存儲的核心,是一個樹結構,表明了內存中一份完整的數據。DataTree不包含任何與網絡、客戶端鏈接及請求處理相關的業務邏輯,是一個獨立的組件。
DataNode是數據存儲的最小單元,其內部除了保存告終點的數據內容、ACL列表、節點狀態以外,還記錄了父節點的引用和子節點列表兩個屬性,其也提供了對子節點列表進行操做的接口。
Zookeeper的內存數據庫,管理Zookeeper的全部會話、DataTree存儲和事務日誌。ZKDatabase會定時向磁盤dump快照數據,同時在Zookeeper啓動時,會經過磁盤的事務日誌和快照文件恢復成一個完整的內存數據庫。
事務日誌指zookeeper系統在正常運行過程當中,針對全部的更新操做,在返回客戶端「更新成功」的響應前,zookeeper會保證已經將本次更新操做的事務日誌已經寫到磁盤上,只有這樣,整個更新操做纔會生效。
臨時(Ephemeral)znode
持久(PERSISTENT)znode
順序(SEQUENTIAL)znode
zxid
zookeeper znode stat 結構
ZooKeeper Sessions
ZooKeeper的每一個客戶端都維護一組服務端信息,在建立鏈接時由應用指定,客戶端隨機選擇一個服務端進行鏈接,鏈接成功後,服務端爲每一個鏈接分配一個惟一標識。客戶端在建立鏈接時能夠指定溢出時間,客戶端會週期性的向服務端發送PING請求來保持鏈接,當客戶端檢測到與服務端斷開鏈接後,客戶端將自動選擇服務端列表中的另外一個服務端進行重連。
建立會話
ZooKeeper zk = new ZooKeeper(serverList, sessionTimeout, watcher);
zk.create("/test", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
複製代碼
建立客戶端session時,應用必須傳入一組以逗號分隔的host:port列表,每一個都對應一個ZooKeeper服務端,ZooKeeper客戶端將選擇任意一個服務端並嘗試與其鏈接(這組serverlist會在初始化的時候打亂),若是鏈接失敗,或者因爲某些緣由致使客戶端與服務端鏈接斷開,客戶端將自動的選擇列表中的另外一個服務端進行鏈接,直到成功。當session建立成功後,ZooKeeper服務端爲session分配一個惟一標識。
client進行tcp創建鏈接
當tcp鏈接成功以後,client發送一個ConnectRequest包,將ZooKeeper構造函數傳入的sessionTimeout數值發給Server。zookeeper server會驗證客戶端發來的sessionTimeout值;zookeeper server中有連個配置項.
(tickTime也是一個配置項。是Server內部控制時間邏輯的最小時間單位)
若是客戶端發來的sessionTimeout超過min-max這個範圍,server會自動截取爲min或max.
server等表決經過後,會爲這個session生成一個password,連同sessionId,sessionTimeOut一塊兒返回給客戶端(ConnectResponse)。客戶端若是須要重連Server,能夠新建一個ZooKeeper對象,將上一個成功鏈接的ZooKeeper 對象的sessionId和password傳給Server ZooKeeper zk = new ZooKeeper(serverList, sessionTimeout, watcher, sessionId,passwd);ZKServer會根據sessionId和password爲同一個client恢復session,若是尚未過時的話。
會話狀態
Zookeeper會話在整個運行期間的生命週期中,會在不一樣的會話狀態中之間進行切換,這些狀態能夠分爲CONNECTING, ASSOCIATING, CONNECTED, CLOSED, AUTH_FAILED。
一旦客戶端開始建立Zookeeper對象,那麼客戶端狀態就會變成CONNECTING狀態,同時客戶端開始嘗試鏈接服務端,鏈接成功後,客戶端狀態變爲CONNECTED,一般狀況下,因爲斷網或其餘緣由,客戶端與服務端之間會出現斷開狀況,一旦碰到這種狀況,Zookeeper客戶端會自動進行重連服務,同時客戶端狀態再次變成CONNCTING,直到從新連上服務端後,狀態又變爲CONNECTED,在一般狀況下,客戶端的狀態老是介於CONNECTING和CONNECTED之間。可是,若是出現諸如會話超時、權限檢查或是客戶端主動退出程序等狀況,客戶端的狀態就會直接變動爲CLOSE狀態。
session激活
在ZooKeeper中,服務器和客戶端之間維持的是一個長鏈接,在 SESSION_TIMEOUT 時間內,服務器會肯定客戶端是否正常鏈接(客戶端會定時向服務器發送heart_beat),服務器重置下次SESSION_TIMEOUT時間。;同時在Zookeeper的實際設計中,只要客戶端有請求發送到服務端,那麼就會觸發一次會話激活,總結下來兩種狀況都會觸發會話激活。
會話清理
leader server的SessionTracker管理線程會管理者session,執行session的過時檢查,若是會話過時就執行清理操做.
會話重連
客戶端鏈接指定根路徑
在ZooKeeper 3.2.0增長了可選的「chroot」後綴,能夠改變當前客戶端的根路徑。例如,若是使用」localhost:2181/app/a」,客戶端將使用」/app/a」做爲其根路徑,全部的路徑都會相對於該路徑。好比操做路徑」/foo/bar」將真正對應到」/app/a/foo/bar」。這個特徵在多租戶環境下是很是有用的,能夠簡化客戶端的應用邏輯。
ZooKeeper Watches
在ZooKeeper中,全部的讀操做(getData,getChildren和exists)均可以設置監聽,一個Watch事件是一個一次性的觸發器,當被設置了Watch的數據發生了改變的時候,則服務器將這個改變發送給設置了Watch的客戶端,以便通知它們。
zookeeper機制的特色
當數據改變的時候,那麼一個Watch事件會產生而且被髮送到客戶端中。可是客戶端只會收到一次這樣的通知,若是之後這個數據再次發生改變的時候,以前設置Watch的客戶端將不會再次收到改變的通知,由於Watch機制規定了它是一個一次性的觸發器。
這個代表了Watch的通知事件是從服務器發送給客戶端的,是異步的,這就代表不一樣的客戶端收到的Watch的時間可能不一樣,可是ZooKeeper有保證:當一個客戶端在看到Watch事件以前是不會看到結點數據的變化的。例如:A=3,此時在上面設置了一次Watch,若是A忽然變成4了,那麼客戶端會先收到Watch事件的通知,而後纔會看到A=4。
znode 節點自己具備不一樣的改變方式,setData() 會觸發設置在某一節點上所設置的數據監視(假定數據設置成功),而一次成功的 create() 操做則會出發當前節點上所設置的數據監視以及父節點的子節點監視。一次成功的 delete() 操做將會觸發當前節點的數據監視和子節點監視事件,同時也會觸發該節點父節點的child watch。WatchEvent是最小的通訊單元,結構上只包含通知狀態、事件類型和節點路徑。ZooKeeper服務端只會通知客戶端發生了什麼,並不會告訴具體內容。
監聽事件類型
ACL 權限控制
zk作爲分佈式架構中的重要中間件,一般會在上面以節點的方式存儲一些關鍵信息,默認狀況下,全部應用均可以讀寫任何節點,在複雜的應用中,這不太安全,ZK經過ACL機制來解決訪問權限問題.
回顧zookeeper架構
ZAB協議
ZAB協議(Zookeeper Atomic Broadcast Protocol)是Zookeeper系統專門設計的一種支持崩潰恢復的原子廣播協議。Zookeeper使用該協議來實現分佈數據一致性並實現了一種主備模式的系統架構來保持各集羣中各個副本之間的數據一致性。採用zab協議的最大目標就是創建一個高可用可擴展的分佈式數據主備系統。即在任什麼時候刻只要leader發生宕機,都能保證分佈式系統數據的可靠性和最終一致性。
特色
ZAB協議工做原理
ZAB協議要求每一個leader都要經歷三個階段,即發現,同步,廣播。
* 發現:即要求zookeeper集羣必須選擇出一個leader進程,同時leader會維護一個follower可用列表。未來客戶端能夠與這個follower中的節點進行通訊。
* 同步:leader要負責將自己的數據與follower完成同步,作到多副本存儲。這樣也是體現了CAP中高可用和分區容錯。follower將隊列中未處理完的請求消費完成後,寫入本地事物日誌中。
* 廣播:leader能夠接受客戶端新的proposal請求,將新的proposal請求廣播給全部的follower。
複製代碼
ZAB兩種模式
當服務初次啓動,或者 leader 節點掛了,系統就會進入恢復模式,直到選出了有合法數量 follower 的新 leader,而後新 leader 負責將整個系統同步到最新狀態。
Zab 協議中,全部的寫請求都由 leader 來處理。正常工做狀態下,leader 接收請求並經過廣播協議來處理。
選舉
問題: 如何選舉leader
某個服務能夠配置爲多個實例共同構成一個集羣對外提供服務。其每個實例本地都存有冗餘數據,每個實例均可以直接對外提供讀寫服務。在這個集羣中爲了保證數據的一致性,須要有一個Leader來協調一些事務。那麼問題來了:如何肯定哪個實例是Leader呢?
分佈式選舉算法
zookeeper選主
搞清楚幾個問題
在ZooKeeper集羣中,Server的信息都在zoo.conf配置文件中,根據配置文件的信息就能夠知道其它Server的信息。
Leader要具備最高的zxid;集羣中大多數的機器(至少n/2+1)獲得響應並follow選出的Leader。
ZooKeeper中每個Server都有一個ID,這個ID是不重複的,若是遇到這樣的狀況時,ZooKeeper就推薦ID最大的哪一個Server做爲Leader。
Leader定時向Fllower發ping消息,Fllower定時向Leader發ping消息,當發現Leader沒法ping通時,就改變本身的狀態(LOOKING),發起新的一輪選舉。
leader選主時機
核心概念
ZooKeeper服務器狀態
myid
每一個Zookeeper服務器,都須要在數據文件夾下建立一個名爲myid的文件,該文件包含整個Zookeeper集羣惟一的ID(整數)。例如某Zookeeper集羣包含三臺服務器,hostname分別爲zoo一、zoo2和zoo3,其myid分別爲一、2和3,則在配置文件中其ID與hostname必須一一對應,以下所示。在該配置文件中,server.後面的數據即爲myid.
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
複製代碼
zxid
每次對Zookeeper的狀態的改變都會產生一個zxid(ZooKeeper Transaction Id),zxid是全局有序的,若是zxid1小於zxid2,則zxid1在zxid2以前發生。爲了保證順序性,該zkid必須單調遞增。所以Zookeeper使用一個64位的數來表示,高32位是Leader的epoch,從1開始,每次選出新的Leader,epoch加一。低32位爲該epoch內的序號,每次epoch變化,都將低32位的序號重置。這樣保證了zkid的全局遞增性。
logicalclock
每一個服務器會維護一個自增的整數,名爲logicalclock,它表示這是該服務器發起的第多少輪投票。
選主步驟
服務器啓動的時候每一個server的狀態時Looking,若是是leader掛掉後進入選舉,那麼餘下的非Observer的Server就會將本身的服務器狀態變動爲Looking,而後開始進入Leader的選舉狀態;
Zookeeper規定全部有效的投票都必須在同一輪次中。每一個服務器在開始新一輪投票時,會先對本身維護的logicalclock進行自增操做。
每一個服務器在廣播本身的選票前,會將本身的投票箱清空。該投票箱記錄了所收到的選票。例:服務器2投票給服務器3,服務器3投票給服務器1,則服務器1的投票箱爲(2, 3), (3, 1), (1, 1)。票箱中只會記錄每一投票者的最後一票,如投票者更新本身的選票,則其它服務器收到該新選票後會在本身票箱中更新該服務器的選票。
每一個server會產生一個(sid,zxid)的投票,系統初始化的時候zxid都是0,若是是運行期間,每一個server的zxid可能都不一樣,這取決於最後一次更新的數據。將投票發送給集羣中的全部機器;
服務器會嘗試從其它服務器獲取投票,並記入本身的投票箱內。若是沒法獲取任何外部投票,則會確認本身是否與集羣中其它服務器保持着有效鏈接。若是是,則再次發送本身的投票;若是否,則立刻與之創建鏈接。
收到外部投票後,首先會根據投票信息中所包含的logicalclock來進行不一樣處理. * 外部投票的logicalclock大於本身的logicalclock。說明該服務器的選舉輪次落後於其它服務器的選舉輪次,當即清空本身的投票箱並將本身的logicalclock更新爲收到的logicalclock,而後再對比本身以前的投票與收到的投票以肯定是否須要變動本身的投票,最終再次將本身的投票廣播出去。 * 外部投票的logicalclock小於本身的logicalclock。當前服務器直接忽略該投票,繼續處理下一個投票。 * 外部投票的logickClock與本身的相等。當時進行選票PK。
對本身的投票和接收到的投票進行PK: 1. 先檢查zxid,較大的優先爲leader; 2. 若是zxid同樣,sid較大的爲leader; 3. 根據PK結果更新本身的投票,在次發送本身的投票;
每次投票後,服務器統計投票信息,若是有過半機器接收到相同的投票,那麼leader產生,若是否,那麼進行下一輪投票;
一旦肯定了Leader,server會更新本身的狀態爲Following或者是Leading。選舉結束。
幾種leader選舉場景
舉例
集羣啓動選舉
Follower重啓選舉
Leader重啓選舉
數據同步
在完成leader選舉階段後,準Leader能夠獲取集羣中最新的提議歷史。準Leader在該階段會把最新的提議歷史同步到集羣中的全部節點。當同步完成時(過半),準Leader纔會真正成爲Leader,執行Leader的工做。
原子廣播
分佈式一致問題
分佈式中有這麼一個疑難問題,客戶端向一個分佈式集羣的服務端發出一系列更新數據的消息,因爲分佈式集羣中的各個服務端節點是互爲同步數據的,因此運行完客戶端這系列消息指令後各服務端節點的數據應該是一致的,但因爲網絡或其餘緣由,各個服務端節點接收到消息的序列可能不一致,最後致使各節點的數據不一致。
分佈式一致性
CAP
分佈式系統的最大難點,就是各個節點的狀態如何同步。CAP 定理是這方面的基本定理,也是理解分佈式系統的起點。
寫操做以後的讀操做,必須返回該值。
意思是隻要收到用戶的請求,服務器就必須給出迴應。每次請求都能獲取到非錯的響應——可是不保證獲取的數據爲最新數據。
區間通訊可能失敗。
這三個基本需求,最多隻能同時知足其中的兩項,一致性和可用性不可能同時成立,由於可能通訊失敗(即出現分區容錯)。
拜占庭問題
11位拜占庭將軍去打仗, 他們各自有權力觀測敵情並做出判斷, 進攻或撤退, 那麼怎麼讓他們只用傳令兵達成一致呢?一種很符合直覺的方法就是投票,每位將軍做出決定後都將結果"廣播"給其他全部將軍, 這樣全部將軍都能得到一樣的11份(包括本身)結果, 取多數, 便可獲得全軍都贊成的行爲.但若是這11位將軍中有間諜呢? 假設有9位忠誠的將軍, 5位判斷進攻, 4位判斷撤退, 還有2個間諜惡意判斷撤退, 雖然結果是錯誤的撤退, 但這種狀況徹底是容許的. 由於這11位將軍依然保持着狀態一致性。
一致性解決方案
2PC和3PC
2PC
第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。
3PC
在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段以前各參與節點的狀態是一致的。引入超時機制,同時在協調者和參與者中都引入超時機制。
區別
相對於2PC,3PC主要解決的單點故障問題,並減小阻塞,由於一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。可是這種機制也會致使數據一致性問題,由於,因爲網絡緣由,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時以後執行了commit操做。這樣就和其餘接到abort命令並執行回滾的參與者之間存在數據不一致的狀況。
總結
不管是二階段提交仍是三階段提交都沒法完全解決分佈式的一致性問題。那麼世上只有一種一致性算法,那就是Paxos,全部其餘一致性算法都是Paxos算法的不完整版。
Paxos
ZAB原子廣播(數據一致原理)
paxos理論到實際是個艱難的過程。好比怎樣在分佈式環境下維持一個全局惟一遞增的序列,若是是靠數據庫的自增主鍵,那麼整個系統的穩定和性能的瓶頸全都集中於這個單點。paxos算法也沒有限制Proposer的個數,Proposer個數越多,那麼達成一致所形成的碰撞將越多,甚至產生活鎖,若是限制Proposer的個數爲一個,那麼就要考慮惟一的Proposer崩潰要怎麼處理。
工做步驟
擴展
Curator是Netflix公司開源的一套Zookeeper客戶端框架。瞭解過Zookeeper原生API都會清楚其複雜度。Curator幫助咱們在其基礎上進行封裝、實現一些開發細節,包括接連重連、反覆註冊Watcher和NodeExistsException等。
總體回顧
思考問題