Reference: http://mp.weixin.qq.com/s?src=3×tamp=1477979201&ver=1&signature=bBZqNrNdHkygiHTZSkKSBvnNNp4oA-ChCmJCKXvfUeAmnIyTEHnqmcNgnAtEj7SB96HuKtPOObEQa7gRZVp9EATnClUg1FMHkFK-bAk*HlgpcqFY5ZtQ35Uz*KRmGOdDO1S7cvjmHUZlKoIoMvLhKNuCNAH1J489oc9q37D60aM=
第一部分: Zookeeper 簡介
Zookeeper 是 Google 的 Chubby一個開源的實現,是 Hadoop 的分布式協調服務
自2010年10月升級成Apache Software Foundation(ASF)頂級項目
分布式協調服務,提供以下功能:
-
組管理服務
-
分布式配置服務
-
分布式同步服務
-
分布式命名服務
誰在使用 Zookeeper
開源軟件
-
HBase 開源的非關系型分布式數據庫
-
Solr Apache Lucene項目的開源企業搜索平台
-
Storm 分布式計算框架
-
Neo4j 高性能的,Nosql圖形數據庫
-
…
公司
-
Yahoo
-
LinkedIn
-
Twitter
-
Taobao
-
…
Zookeeper 架構
-
客戶端隨機連接集群中的任何一台 server
-
集群內所有的 server 基於 Zab(ZooKeeper Atomic Broadcast)協議通信
-
集群內部根據算法自動選舉出 leader, 負責向 follower 廣播所有變化消息
-
集群中每個follower 都和 leader 通信
-
follower 接收來自 leader 的所有變化消息,保存在自己的內存中
-
follower 轉發來自客戶端的寫請求給 leader
-
客戶端的讀請求會在 follower 端直接處理,無需轉發給 leader
Zookeeper 數據模型
-
基於樹形結構的命名空間,與文件系統類似
-
節點(znode)都可以存儲數據,可以有子節點
-
節點不支持重命名
-
數據大小不超過1MB(可配置)
-
數據讀寫保持完整性
Zookeeper 基本 API
Znode 節點類型
-
PERSISTENT
-
PERSISTENT_SEQUENTIAL
-
EPHEMERAL
-
EPHEMERAL_SEQUENTIAL
PERSISTENT(0, false, false), PERSISTENT_SEQUENTIAL(2, false, true), EPHEMERAL(1, true, false), EPHEMERAL_SEQUENTIAL(3, true, true);
Sequential/non-sequential節點類型
-
Non-sequential節點不能有重名
-
Sequential節點
-
創建時可重名
-
實際生成節點名末尾自動添加一個10位長度,左邊以0填充的單遞增數字
Ephemeral/Persistent節點類型
-
Ephemeral節點在客戶端session結束或超時后自動刪除
-
Persistent節點生命周期和session無關, 只能顯式刪除
注意:EPHEMERAL類型的目錄節點不能有子節點目錄
ZooKeeper Session
-
客戶端和 server 間采用長連接
-
連接建立后, server生成 session id(64位)返還給客戶端
-
客戶端定期發送 ping 包來檢查和保存和 server 的連接
-
一旦 session 結束或超時,所有 ephemeral節點會被刪除
-
客戶端可根據情況設置合適的 session 超時時間
Zookeeper Watcher
-
Watch 是客戶端安裝在 server 的事件監聽方法
-
當監聽的節點發生變化, server 將通知所有注冊的客戶端
-
客戶端使用單線程對所有時間按順序同步回調
-
觸發回調條件:
-
客戶端連接,斷開連接
-
節點數據發生變化
-
節點本身發生變化
注意:
-
Watch 是單次的,每次觸發后會被自動刪除
-
如果需要再次監聽時間,必須重新安裝 Watch
Watch 的創建和觸發規則
-
在讀操作 exists、getChildren和getData上可以設置觀察,這些觀察可以被寫操作create、delete和setData觸發
第二部分:Zooleeper 應用
-
分布式配置管理
-
高可用分布式集群
-
分布式隊列
-
分布式鎖
分布式配置實現
-
將配置文件信息保存在 Server 某個目錄節點中,然后所有需要修改的應用及其監控配置信息的狀態
-
一旦配置信息發生變化,每台應用將會收到 server 的通知,然后從 server 獲取最新的配置信息到系統中
生產配置發布系統解決方案
Zookeeper + Dubbo
高可用分布式集群方案(下回分解)
集群管理
數據庫宕機檢測方案
-
使用定時任務監控 oracle 進程(端口),開機自啟動;
-
如果監測到有 oracle 進程,調用 monitor service,退出定時任務;
-
monitor service 負責監聽oracle 端口;
-
monitor 依賴於 zookeeperclient service;
-
zookeeperclient service 負責連接 Zookeeper server 創建臨時節點;
-
當 oracle 進程退出時,monitor service 停止 zookeeperclient service
-
zookeeperclient service 與 server 斷開后,server 自動刪除臨時節點;
-
monitor service 向管理員郵箱發送數據庫宕機郵件,寫入日志;
-
當 數據庫服務器宕機時,zookeeperclient service與 Server 斷開連接,自動刪除臨時節點
具體實現
zookeeperclient.sh
#!/bin/bash
# /etc/init.d/zookeeperclient.sh
case "$1" in
start)
echo "Starting Zookeeper Client"
python /home/master/zookeeperclient.py &
;;
stop)
echo "Stopping Zookeeper Client"
#killall zookeeperclient.py
kill $(ps aux | grep -m 1 'python /home/master/zookeeperclient.py' | awk '{ print $2 }')
;;
*)
echo "Usage: service Client start|stop"
exit 1
;;
esac
exit 0
zookeeperclient.py
#!/usr/local/bin/python3from kazoo.client import KazooClientimport time zk = KazooClient(hosts = '192.168.43.125:2181',connection_retry = True) zk.start() zk.create("/datasource/datasource1", b"DATASOURCE1", None, True, False, True)while True: try: time.sleep(2) except (KeyboardInterrupt, SystemExit): zk.stop()
monitor.sh
#!/bin/bash
# /etc/init.d/monitor.sh
case "$1" in
start)
python3 /Users/codeai/Desktop/monitor.py &
sleep 2
if [ "$(ps -ef | grep zookeeperclient.py | grep -v grep | awk '{ print $2 }')" = ""]
then
exit 1
else
python3 /Users/codeai/Desktop/zookeeperclient.py &
fi
;;
stop)
kill $(ps -ef | grep zookeeperclient.py | grep -v grep | awk '{ print $2 }')
sleep 2
kill $(ps -ef | grep monitor.py | grep -v grep | awk '{ print $2 }')
;;
*)
echo "Usage: service command start|stop"
exit 1
;;
esac
exit 0
monitor.py
def socketmronitor(): line = "127.0.0.1 8080" ip = line.split()[0] port = int(line.split()[1]) while True: try: sc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print(ip, port) sc.settimeout(2) sc.connect((ip, port)) timenow = time.localtime() datenow = time.strftime('%Y-%m-%d %H:%M:%S', timenow) print("%s:%s 連接成功->%s \n" % (ip, port, datenow)) sc.close() time.sleep(3) except Exception as e: file = open("socketmonitor_err.log", "a") timenow = time.localtime() datenow = time.strftime('%Y-%m-%d %H:%M:%S', timenow) file.write("%s:%s 連接失敗->%s \n" % (ip, port, datenow)) file.close() send_mail(['348055827@qq.com'], 'Oracle quit', sysinfo()) exit(-1)
server端監聽節點變化
Watcher wh = new Watcher() { public void process(WatchedEvent event) { // 如果發生了"/datasource"節點下的子節點變化事件, 更新server列表, 並重新注冊監聽 if (event.getType() == Event.EventType.NodeChildrenChanged && ("/" + groupNode).equals(event.getPath())) { try { updateServerList(); } catch (Exception e) { e.printStackTrace(); } } } }; zk = new ZooKeeper("127.0.0.1:2181", Integer.MAX_VALUE, wh);
分布式隊列
先說一個通俗易懂的例子:
一個老中醫每天只看5個病人,當第一個病人已經掛號(向 Service 注冊並且創建了一個節點),但是並未達到5個人的要求,所以這個病人就等待,知道第5個人掛號后,這5個人才會到老中醫那兒去看病.
這個就可以簡單的理解為分布式隊列,5個人是不同 client, 他們都必須等到所有人都向 Zookeeper Server 創建了一個節點,(他們通過對節點的監控從而知曉是否達到5個人),當創建了5個節點后,通過 Watcher 創建一個 start 的節點標識,告知所有的 client, 這個隊列已經可以使用了.
具體實現
創建一個父目錄 /synchronizing,每個成員都監控目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列(創建 /synchronizing/member_i 的臨時目錄節點),然后每個成員獲取 / synchronizing 目錄的所有目錄節點,判斷 i 的值是否已經是成員的個數,如果小於成員個數等待 /synchronizing/start 的出現,如果已經相等就創建 /synchronizing/start
分布式鎖
還是先說一個通俗易懂的例子:
有個科室的女醫生很漂亮,很多人慕名前來,但是她一次只會給一個人看病,也是有這么5個人,先去掛號(向 Server 注冊),然后每個人拿到了P1,P2…P5編號的牌子.護士從小號開始喊,叫到誰誰就去找女醫生玩兒(聽護士喊話,然后對比自己手上的號拍,如果叫到的跟自己手上的號牌一樣,則說明獲得了鎖),其他人則等待,
當上一個病人看完病離開之后,將手上的號牌扔掉,護士繼續喊號,重復上面的過程.
具體實現
Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用 exists(String path, boolean watch) 方法並監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。
總結
-
文件系統是一個樹形的文件系統,但比linux系統簡單,不區分文件和文件夾,所有的文件統一稱為znode
-
znode的作用:存放數據,但上限是1M ;存放ACL(access control list)訪問控制列表
-
數據模型:短暫znode( zkCli.sh 客戶端斷掉連接之后,就不存在了)和持久znode
-
順序號:每個znode在創建的時候都會編一個號,按照那他們就會按照創建的時間編號,同樣的父節點的znode共用一套編號
-
觀察(watch):觀察子節點的變化,類似於我們傳統關系型數據庫中的觸發器
-
操作:create delete setData getData exists(znode是否存在並且查詢他的data) getACL setACL
-
集群是無中心的,只要有超過一半以上的節點沒有down掉就能工作,出於這個原因,我們zookeeper的節點數通常要設置成奇數。
-
在整個集群沒有down掉之前,至少有一個節點是最新的狀態,這是通過Zap,該zap協議是包括2個無限重復的階段:
-
選舉(選舉出一個leader,其他節點就變成了follower)
-
原子廣播(所有寫請求轉發給leader,再由leader廣播給各個follower,當半數以上follower將修改持久化后,leader才會提交這個更新,接下來客戶端才會收到一個更新成功的響應)
-
應用:
-
配置文件的同步(放一個watch,檢測到數據改動后,立刻同步到其他節點,確保配置文件的一致性)
-
鎖機制的實現,比如很多客戶端訪問一個znode的資源,先給這個znode設置一個觀察,觀察節點的刪除,其他客戶端進來后,會添加一個對應的短暫znode,因為zookeeper的順序號機制,給每個要訪問資源的客戶端對應的znode分配一個順序號,通過對比順序號,決定能不能使用這個資源