ZooKeeper是一個高可用的分佈式數據管理與協調框架mysql
- ZAB算法的實現,很好的保證了分佈式系統數據一致性
數據發佈/訂閱web
- 數據發佈/訂閱系統,即所謂配置中心
- 發佈者發佈數據到ZooKeeper的一系列節點上,供訂閱者訂閱,達到動態更新數據的目的
- 實現數據的集中式管理和動態更新
發佈/訂閱系統通常有推(Push)和拉(Pull)兩種模式算法
- 推(Push):服務端主動將數據推送給客戶端
- 拉(Pull):客戶端主動去服務端拉取最新數據,通常客戶端採起定時輪詢的策略
- ZooKeeper纔去的是推拉結合的策略
- 客戶端向服務端註冊須要關注的節點,服務端數據發生變化的時候向客戶端推送watcher事件通知,客戶端再主動去服務端拉取數據
負載均衡sql
- 常見的計算機網絡技術,對多臺計算機、CPU、磁盤驅動器等分配負載;
- 達到優化資源使用、最小化響應時間、最大化吞吐率、避免過載的目的
- 分爲軟負載和硬負載;ZooKeeper屬於軟件負載
比較典型的是DNS 服務:數據庫
- DNS是(Domain Name System)域名系統的縮寫
- 能夠看作是一個超大規模的分佈式映射表(域名-->IP),方便人們經過域名訪問互聯網站點
- 實際開發中一般採用本地host 綁定來實現域名解析
基於ZooKeeper實現的動態域名解析方案(DDNS :Dynamic DNS):編程
- 域名解析由每一個應用本身解決;
- 每一個應用還會在數據節點上註冊一個數據變動Watcher,以便收到變動通知;
- 好處:
- 一方面避免帶來域名無限增加帶來的集中式管理壓力
- 另外一方面域名變動,逐臺更新本地HOST的繁瑣
![](http://static.javashuo.com/static/loading.gif)
自動化的DNS服務器服務器
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
- 整個框架是域名解析的服務提供者,須要域名解析的客戶屬於消費者
系統運行過程:網絡
- 域名註冊
- 每一個服務啓動過程當中,把本身的域名信息註冊到Register集羣
- Register將信息寫入ZooKeeper對應域名數據節點
- 域名解析:
- 消費者向Dispatcher 發起查詢,Dispatcher返回結果
- 域名探測:
- DDNS系統對全部註冊的域名進行可用性檢測(健康度檢測)
- 分兩種:
- 服務端主動發起健康度心跳檢測,通常須要服務端和客戶端創建TCP長鏈接
- 客戶端發起健康度心跳檢測
- DDNS 採用的是每一個服務提供者定時向Scanner 彙報健康度的形式(第二種)
- Scanner會記錄時間,一旦超過5秒沒收到狀態彙報,就會清理域名
命名服務(Name Service)多線程
- 命名服務是分佈式系統最基本的公共服務之一
- 分佈式系統中,被命名實體:集羣中的機器,提供的服務地址或遠程對象等,均可稱爲名字(Name)
- 比較常見的如:分佈式服務框架(RPC、RMI),客戶端程序經過名字能夠獲取資源實體、服務地址、提供者信息等
- ZooKeeper命名服務,經過資源引用的方式對資源進行定位和使用
ZooKeeper實現分佈式全局惟一ID分配機制併發
- 在單庫單表數據庫中,auto_increment 就好
- 分佈式環境下全局惟一ID:
- UUID:universial unique identity,通用惟一識別碼
- GUID:Global unique identity,是hibernate 對UUID 的實現
- UUID 能很是簡便的實現惟一性,存在缺陷:
- 包含32位字符和4個短線字符串,長度過長
- 含義不明
- ZooKeeper全局惟一id生成:
- 客戶端根據任務類型,在各自任務類型下順序生成id
- 節點名拼接上類型,獲取完整全局惟一id
![](http://static.javashuo.com/static/loading.gif)
分佈式協調/通知
- 分佈式協調/通知服務,是分佈式系統必不可缺環節,不一樣分佈式組件有機結合起來關鍵所在
- 對於多臺機器上部署運行的應用,須要一個協調者(coordinator)
- 從應用中抽離出來分佈式協調
- 解耦各個系統之間耦合性,加強擴展性
- ZooKeeper中特有的Watcher機制和異步通知機制
mysql數據複製總線(mysql_replicator)
- 實時數據複製框架,在不一樣mysql數據庫實例之間進行實時異步數據複製和數據變化通知
- 整個系統包括:mysql數據庫集羣、消息隊列系統、任務管理監控平臺、ZooKeeper集羣等組件
- 包含數據生產者、複製管道、數據消費者等部分
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
- 每一個模塊獨立進程部署在服務端
- 運行時數據和配置信息均保存在ZooKeeper
- web控制檯根據ZooKeeper上的信息獲取到後臺數據,發佈控制信息
![](http://static.javashuo.com/static/loading.gif)
任務註冊:
- core啓動的時候首先向數據節點(/mysql_replicator/taskes)註冊」任務列表節點「
任務熱備份
- 爲了應對複製中可能出現的問題(如:複製任務故障或複製任務主機故障)
- 複製組件採用熱備份的容災形式,即將同一個複製任務,部署在不一樣的主機上
- 主、備任務主機經過ZooKeeper相互檢查健康情況
- 下圖中,HostName一、2這樣的臨時順序節點
- 完成子節點建立後,每臺機器都能獲取本身的節點;
- 對比判斷本身是不是最小節點,最小狀態RUNNING、其餘STANDBY(小序號優先策略)
熱備切換
- RUNNING正常複製、STANDBY待命
- RUNNING故障,STANDBY最小節點變成RUNNING
- 具體實現:全部STANDBY註冊子節點數據變動Watcher 到RUNNING機器
- RUNNING機器與ZooKeeper斷開鏈接,對應數據節點就會消失,Watcher會受到事件
![](http://static.javashuo.com/static/loading.gif)
記錄執行狀態
- RUNNING機器將運行時的任務上下文狀態保留給STANDBY機器
- 寫入lastCommit節點
控制檯協調
- Server管理core組件,將其複製相關元數據,寫入數據節點,以備其餘機器共享
冷備切換
- core進程被分配對應Group,掃描組下task列表,以下圖:
![](http://static.javashuo.com/static/loading.gif)
冷熱備份對比
- 熱備份:每一個任務分配兩臺機器,Watcher機制交互,實時性強,資源浪費大
- 冷備份:掃描的方式,下降了實時性,節省資源
一種分佈式系統機器間通訊方式:
- 機器間通訊無外乎:心跳檢測、工做進度彙報、系統調度三種類型
- 心跳檢測
- 一般機器間相互創建鏈接,肯定心跳(對方是否依然存活)
- 使用ZooKeeper實現,各個機器都在ZooKeeper指定位置建立臨時節點,根據臨時節點判斷對應客戶端是否存活
- 工做進度彙報
- 任務分發系統,會把任務分發給不一樣的機器,各個機器向其彙報任務進度
- 每一個任務客戶端都在這個節點下面建立臨時子節點,這樣不只能夠判斷機器是否存活,
- 同時各個機器能夠將本身的任務執行進度寫到該臨時節點中去,以便中心繫統可以實時獲取任務的執行進度
- 系統調度
- Zookeeper可以實現以下系統調度模式:分佈式系統由控制檯和一些客戶端系統兩部分構成
- 控制檯的職責就是須要將一些指令信息發送給全部的客戶端,以控制他們進行相應的業務邏輯
- 後臺管理人員在控制檯上作一些操做,實際上就是修改Zookeeper上某些節點的數據
- Zookeeper能夠把數據變動以事件通知的形式發送給訂閱客戶端
集羣管理
- 集羣監控和集羣控制
- Zookeeper的兩大特性:
- 客戶端若是對Zookeeper的數據節點註冊Watcher監聽,那麼當該數據節點內容或是其子節點列表發生變動時,Zookeeper服務器就會向訂閱的客戶端發送變動通知。
- 對在Zookeeper上建立的臨時節點,一旦客戶端與服務器之間的會話失效,那麼臨時節點也會被自動刪除。
分佈式日誌收集系統
- 重點看收集器模塊
- 日誌源機器分爲多個組,每一個組有一個收集器(收集機器)
一般須要解決兩個問題:
一、註冊收集器機器
- 每一個收集器機器啓動時都會在收集器節點下建立本身的節點,如/logs/collector/[Hostname]
![](http://static.javashuo.com/static/loading.gif)
二、任務分發
- 系統根據收集器節點下子節點的個數,將全部日誌源機器分紅對應的若干組
- 將分組後的日誌源機器列表分別寫到這些收集器機器建立的子節點
- 收集器機器就可以根據本身對應的收集器節點上獲取日誌源機器列表,進而開始進行日誌收集工做。
三、狀態彙報
- 完成任務分發後,機器隨時會宕機
- 每一個收集器機器上建立完節點後,還須要再對應子節點上建立一個狀態子節點,如/logs/collector/host/status
- 每一個收集器機器都須要按期向該結點寫入本身的狀態信息,這可看作是心跳檢測機制,一般收集器機器都會寫入日誌收集進度信息
- 志系統經過判斷狀態子節點最後的更新時間來肯定收集器機器是否存活。
四、動態分配
- 若收集器機器宕機,則須要動態進行收集任務的分配
- 收集系統運行過程當中關注/logs/collector節點下全部子節點的變動,一旦有機器中止彙報或有新機器加入,就開始進行任務的從新分配
- 一般由兩種作法:
- 全局動態分配:對全部的日誌源機器從新進行一次分組,而後將其分配給剩下的收集器機器。
- 局部動態分配:
- 每一個收集器機器在彙報本身日誌收集狀態的同時,也會把本身的負載彙報上去
- 若是一個機器宕機了,那麼日誌系統就會把以前分配給這個機器的任務從新分配到那些負載較低的機器,
- 一樣,若是有新機器加入,會從那些負載高的機器上轉移一部分任務給新機器。
上述步驟已經完整的說明了整個日誌收集系統的工做流程,其中有兩點注意事項:
- 節點類型
- 在/logs/collector節點下建立臨時節點能夠很好的判斷機器是否存活,可是,若機器掛了,其節點會被刪除,記錄在節點上的日誌源機器列表也被清除,
- 因此須要選擇持久節點來標識每一臺機器,同時在節點下分別建立/logs/collector/[Hostname]/status節點來表徵每個收集器機器的狀態,
- 這樣,既能實現對全部機器的監控,同時機器掛掉後,依然可以將分配任務還原。
- 日誌系統節點監聽
- 若採用Watcher機制,收集器節點狀態變動可能很頻繁,那麼通知的消息量的網絡開銷很是大,
- 須要採用日誌系統主動輪詢收集器節點的策略,這樣能夠節省網絡流量,可是存在必定的延時(考慮分佈式日誌收集系統定位,這點延時可接受)。
在線雲主機管理
![](http://static.javashuo.com/static/loading.gif)
- 解決:
- 採用ZooKeeper,將指定的Agent 部署到被監控機器上去,機器啓動,會在ZooKeeper添加臨時節點(這樣斷開時,臨時節點也會被刪除)
- Agent定時寫入本身狀態
![](http://static.javashuo.com/static/loading.gif)
Master選舉
- 在分佈式系統中,Master每每用來協調集羣中其餘系統單元,具備對分佈式系統狀態變動的決定權,
- 如在讀寫分離的應用場景中,客戶端的寫請求每每是由Master來處理,
- 或者其經常處理一些複雜的邏輯並將處理結果同步給其餘系統單元。
- 利用Zookeeper的強一致性,
- 可以很好地保證在分佈式高併發狀況下節點的建立必定可以保證全局惟一性,
- 即Zookeeper將會保證客戶端沒法重複建立一個已經存在的數據節點。
![](http://static.javashuo.com/static/loading.gif)
- 具體過程:
- 首先建立/master_election/2013-09-20節點,客戶端集羣天天會定時往該節點下建立臨時節點,如/master_election/2013-09-20/binding,
- 這個過程當中,只有一個客戶端可以成功建立,此時其變成master,
- 其餘節點都會在節點/master_election/22013-09-20上註冊一個子節點變動的Watcher,用於監控當前的Master機器是否存活,
- 一旦發現當前Master掛了,其他客戶端將會從新進行Master選舉。
![](http://static.javashuo.com/static/loading.gif)
分佈式鎖
- 分佈式鎖用於控制分佈式系統之間同步訪問共享資源的一種方式,
- 能夠保證不一樣系統訪問一個或一組資源時的一致性,主要分爲排他鎖和共享鎖。
- 排他鎖又稱爲寫鎖或獨佔鎖
- 若事務T1對數據對象O1加上了排它鎖,那麼在整個加鎖期間,
- 只容許事務T1對O1進行讀取和更新操做,其餘任何事務都不能再對這個數據對象進行任何類型的操做,直到T1釋放了排它鎖
- 獲取鎖,在須要獲取排它鎖時,全部客戶端經過調用接口,在/exclusive_lock節點下建立臨時子節點/exclusive_lock/lock。
- Zookeeper能夠保證只有一個客戶端可以建立成功,沒有成功的客戶端須要註冊/exclusive_lock節點監聽。
- 釋放鎖,當獲取鎖的客戶端宕機或者正常完成業務邏輯都會致使臨時節點的刪除,
- 此時,全部在/exclusive_lock節點上註冊監聽的客戶端都會收到通知,能夠從新發起分佈式鎖獲取。
![](http://static.javashuo.com/static/loading.gif)
- 共享鎖又稱爲讀鎖
- 若事務T1對數據對象O1加上共享鎖,那麼當前事務只能對O1進行讀取操做,
- 其餘事務也只能對這個數據對象加共享鎖,直到該數據對象上的全部共享鎖都被釋放。
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
羊羣效應(驚羣效應)
- 能夠看到,host1客戶端在移除本身的共享鎖後,Zookeeper發送了子節點更變Watcher通知給全部機器,
- 然而除了給host2產生影響外,對其餘機器沒有任何做用。
- 大量的Watcher通知和子節點列表獲取兩個操做會重複運行,這樣會形成系能鞥影響和網絡開銷,
- 更爲嚴重的是,若是同一時間有多個節點對應的客戶端完成事務或事務中斷引發節點小時,Zookeeper服務器就會在短期內向其餘全部客戶端發送大量的事件通知,這就是所謂的羊羣效應。
![](http://static.javashuo.com/static/loading.gif)
能夠有以下改動來避免羊羣效應:
- 客戶端調用create接口常見相似於/shared_lock/[Hostname]-請求類型-序號的臨時順序節點。
- 客戶端調用getChildren接口獲取全部已經建立的子節點列表(不註冊任何Watcher)。
- 若是沒法獲取共享鎖,就調用exist接口來對比本身小的節點註冊Watcher。
- 對於讀請求:向比本身序號小的最後一個寫請求節點註冊Watcher監聽。
- 對於寫請求:向比本身序號小的最後一個節點註冊Watcher監聽。
- 等待Watcher通知,繼續進入步驟2。
此方案改動主要在於:每一個鎖競爭者,只須要關注/shared_lock節點下序號比本身小的那個節點是否存在便可。
![](http://static.javashuo.com/static/loading.gif)
注意:
- 改進方案主要是縮小鎖做用範圍,和多線程併發編程思路同樣
- 改進後方案比較麻煩
- 並非說非要用改進後的方案:
- 若是集羣規模不大,資源相對沒那麼緊張,第一種方案簡單直接
- 若是規模達到必定程度,精細管理鎖的做用範圍顯得很重要
分佈式隊列
分佈式隊列能夠簡單分爲先入先出隊列模型和等待隊列元素彙集後統一安排處理執行的Barrier模型。
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
Barrier分佈式屏障
![](http://static.javashuo.com/static/loading.gif)
1. 經過調用getData接口獲取/queue_barrier節點的數據內容,如10。
2. 經過調用getChildren接口獲取/queue_barrier節點下的全部子節點,同時註冊對子節點變動的Watcher監聽。
3. 統計子節點的個數。
4. 若是子節點個數還不足10個,那麼須要等待。
5. 接受到Wacher通知後,重複步驟3。
![](http://static.javashuo.com/static/loading.gif)