Zookeeper相关知识
一.Zookeeper是什么?
Zookeeper 分布式服務框架是 Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等。
Zookeeper 作為 Hadoop 項目中的一個子項目,是 Hadoop 集群管理的一個必不可少的模塊,它主要用來控制集群中的數據,如它管理 Hadoop 集群中的 NameNode,還有 Hbase 中 Master Election、Server 之間狀態同步等。
二.Zookeeper能做什么?
Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統的一致性問題,它能提供基于類似于文件系統的目錄節點樹方式的數據存儲,但是 Zookeeper 并不是用來專門存儲數據的,它的作用主要是用來維護和監控你存儲的數據的狀態變化。通過監控這些數據狀態的變化,從而可以達到基于數據的集群管理。
ZooKeeper 主要是用來維護和監控一個目錄節點樹中存儲的數據的狀態,所有我們能夠操作 ZooKeeper 的也和操作目錄節點樹大體一樣,如創建一個目錄節點,給某個目錄節點設置數據,獲取某個目錄節點的所有子目錄節點,給某個目錄節點設置權限和監控這個目錄節點的狀態變化。
Zookeeper 從設計模式角度來看,是一個基于觀察者模式設計的分布式服務管理框架,它負責存儲和管理大家都關心的數據,然后接受觀察者的注冊,一旦這些數據的狀態發生變化,Zookeeper 就將負責通知已經在 Zookeeper 上注冊的那些觀察者做出相應的反應,從而實現集群中類似 Master/Slave 管理模式,關于 Zookeeper 的詳細架構等內部細節可以閱讀 Zookeeper 的源碼。
三.Zookeeper環境如何搭建?
安裝和配置詳解
本文介紹的 Zookeeper 是以 3.2.2 這個穩定版本為基礎,最新的版本可以通過官網http://hadoop.apache.org/zookeeper/來獲取,Zookeeper 的安裝非常簡單,下面將從單機模式和集群模式兩個方面介紹 Zookeeper 的安裝和配置。
單機模式
單機安裝非常簡單,只要獲取到 Zookeeper 的壓縮包并解壓到某個目錄如:/home/zookeeper-3.2.2 下,Zookeeper 的啟動腳本在 bin 目錄下,Linux 下的啟動腳本是 zkServer.sh,在 3.2.2 這個版本 Zookeeper 沒有提供 windows 下的啟動腳本,所以要想在 windows 下啟動 Zookeeper 要自己手工寫一個,如清單 1 所示:
清單 1. Windows 下 Zookeeper 啟動腳本
setlocal set ZOOCFGDIR=%~dp0%..\conf set ZOO_LOG_DIR=%~dp0%.. set ZOO_LOG4J_PROP=INFO,CONSOLE set CLASSPATH=%ZOOCFGDIR% set CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH% set CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH% set ZOOCFG=%ZOOCFGDIR%\zoo.cfg set ZOOMAIN=org.apache.zookeeper.server.ZooKeeperServerMain java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* endlocal?
在你執行啟動腳本之前,還有幾個基本的配置項需要配置一下,Zookeeper 的配置文件在 conf 目錄下,這個目錄下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是將 zoo_sample.cfg 改名為 zoo.cfg,因為 Zookeeper 在啟動時會找這個文件作為默認配置文件。下面詳細介紹一下,這個配置文件中各個配置項的意義。
tickTime=2000 dataDir=D:/devtools/zookeeper-3.2.2/build clientPort=2181?
- tickTime:這個時間是作為 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。
- dataDir:顧名思義就是 Zookeeper 保存數據的目錄,默認情況下,Zookeeper 將寫數據的日志文件也保存在這個目錄里。
- clientPort:這個端口就是客戶端連接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求。
當這些配置項配置好后,你現在就可以啟動 Zookeeper 了,啟動后要檢查 Zookeeper 是否已經在服務,可以通過 netstat – ano 命令查看是否有你配置的 clientPort 端口號在監聽服務。
集群模式
Zookeeper 不僅可以單機提供服務,同時也支持多機組成集群來提供服務。實際上 Zookeeper 還支持另外一種偽集群的方式,也就是可以在一臺物理機上運行多個 Zookeeper 實例,下面將介紹集群模式的安裝和配置。
Zookeeper 的集群模式的安裝和配置也不是很復雜,所要做的就是增加幾個配置項。集群模式除了上面的三個配置項還要增加下面幾個配置項:
initLimit=5 syncLimit=2 server.1=192.168.211.1:2888:3888 server.2=192.168.211.2:2888:3888- initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這里所說的客戶端不是用戶連接 Zookeeper 服務器的客戶端,而是 Zookeeper 服務器集群中連接到 Leader 的 Follower 服務器)初始化連接時最長能忍受多少個心跳時間間隔數。當已經超過 10 個心跳的時間(也就是 tickTime)長度后 Zookeeper 服務器還沒有收到客戶端的返回信息,那么表明這個客戶端連接失敗。總的時間長度就是 5*2000=10 秒
- syncLimit:這個配置項標識 Leader 與 Follower 之間發送消息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 2*2000=4 秒
- server.A=B:C:D:其中 A 是一個數字,表示這個是第幾號服務器;B 是這個服務器的 ip 地址;C 表示的是這個服務器與集群中的 Leader 服務器交換信息的端口;D 表示的是萬一集群中的 Leader 服務器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執行選舉時服務器相互通信的端口。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。
除了修改 zoo.cfg 配置文件,集群模式下還要配置一個文件 myid,這個文件在 dataDir 目錄下,這個文件里面就有一個數據就是 A 的值,Zookeeper 啟動時會讀取這個文件,拿到里面的數據與 zoo.cfg 里面的配置信息比較從而判斷到底是那個 server。
四.Zookeeper維護及其他操作常用命令
4.1 Zookeeper?Server常用命令
啟動Zookeeper服務: bin/zkServer.sh start
查看Zookeeper服務狀態: bin/zkServer.sh status
停止Zookeeper服務: bin/zkServer.sh stop
重啟Zookeeper服務: bin/zkServer.sh restart
連接服務器: zkCli.sh -server 127.0.0.1:2181
查看根目錄 ls /
創建 testnode節點,關聯字符串"HAO" create /zk/testnode "HAO"
查看節點內容 ?get /zk/testnode
設置節點內容 set /zk/testnode abc
刪除節點 delete /zk/testnode
?
4.2 Zookeeper客戶端命令
ZooKeeper命令行工具類似于Linux的shell環境,不過功能肯定不及shell啦,但是使用它我們可以簡單的對ZooKeeper進行訪問,數據創建,數據修改等操作. 使用 zkCli.sh -server 127.0.0.1:2181 連接到 ZooKeeper 服務,連接成功后,系統會輸出 ZooKeeper 的相關環境以及配置信息。命令行工具的一些簡單操作如下:
?
4.3?ZooKeeper 常用四字命令
ZooKeeper 支持某些特定的四字命令字母與其的交互。它們大多是查詢命令,用來獲取 ZooKeeper 服務的當前狀態及相關信息。用戶在客戶端可以通過 telnet 或 nc 向 ZooKeeper 提交相應的命令
1. 可以通過命令:echo stat|nc 127.0.0.1 2181 來查看哪個節點被選擇作為follower或者leader 2. 使用echo ruok|nc 127.0.0.1 2181 測試是否啟動了該Server,若回復imok表示已經啟動。 3. echo dump| nc 127.0.0.1 2181 ,列出未經處理的會話和臨時節點。 4. echo kill | nc 127.0.0.1 2181 ,關掉server5. echo conf | nc 127.0.0.1 2181 ,輸出相關服務配置的詳細信息。 6. echo cons | nc 127.0.0.1 2181 ,列出所有連接到服務器的客戶端的完全的連接 / 會話的詳細信息。 7. echo envi |nc 127.0.0.1 2181 ,輸出關于服務環境的詳細信息(區別于 conf 命令)。 8. echo reqs | nc 127.0.0.1 2181 ,列出未經處理的請求。9. echo wchs | nc 127.0.0.1 2181 ,列出服務器 watch 的詳細信息。 10. echo wchc | nc 127.0.0.1 2181 ,通過 session 列出服務器 watch 的詳細信息,它的輸出是一個與 watch 相關的會話的列表。 11. echo wchp | nc 127.0.0.1 2181 ,通過路徑列出服務器 watch 的詳細信息。它輸出一個與 session 相關的路徑。?
五.Zookeeper常見應用場景
?
-
統一命名服務(Name Service)
?
分布式應用中,通常需要有一套完整的命名規則,既能夠產生唯一的名稱又便于人識別和記住,通常情況下用樹形的名稱結構是一個理想的選擇,樹形的名稱結構是一個有層次的目錄結構,既對人友好又不會重復。說到這里你可能想到了 JNDI,沒錯 Zookeeper 的 Name Service 與 JNDI 能夠完成的功能是差不多的,它們都是將有層次的目錄結構關聯到一定資源上,但是 Zookeeper 的 Name Service 更加是廣泛意義上的關聯,也許你并不需要將名稱關聯到特定資源上,你可能只需要一個不會重復名稱,就像數據庫中產生一個唯一的數字主鍵一樣。
?
Name Service 已經是 Zookeeper 內置的功能,你只要調用 Zookeeper 的 API 就能實現。如調用 create 接口就可以很容易創建一個目錄節點。
?
-
配置管理(Configuration Management)
?
配置的管理在分布式應用環境中很常見,例如同一個應用系統需要多臺 PC Server 運行,但是它們運行的應用系統的某些配置項是相同的,如果要修改這些相同的配置項,那么就必須同時修改每臺運行這個應用系統的 PC Server,這樣非常麻煩而且容易出錯。
?
像這樣的配置信息完全可以交給 Zookeeper 來管理,將配置信息保存在 Zookeeper 的某個目錄節點中,然后將所有需要修改的應用機器監控配置信息的狀態,一旦配置信息發生變化,每臺應用機器就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應用到系統中。
?
圖 2. 配置管理結構圖
-
集群管理(Group Membership)
?
Zookeeper 能夠很容易的實現集群管理的功能,如有多臺 Server 組成一個服務集群,那么必須要一個“總管”知道當前集群中每臺機器的服務狀態,一旦有機器不能提供服務,集群中其它集群必須知道,從而做出調整重新分配服務策略。同樣當增加集群的服務能力時,就會增加一臺或多臺 Server,同樣也必須讓“總管”知道。
?
Zookeeper 不僅能夠幫你維護當前的集群中機器的服務狀態,而且能夠幫你選出一個“總管”,讓這個總管來管理集群,這就是 Zookeeper 的另一個功能 Leader Election。
?
它們的實現方式都是在 Zookeeper 上創建一個 EPHEMERAL 類型的目錄節點,然后每個 Server 在它們創建目錄節點的父目錄節點上調用getChildren(String?path, boolean?watch) 方法并設置 watch 為 true,由于是 EPHEMERAL 目錄節點,當創建它的 Server 死去,這個目錄節點也隨之被刪除,所以 Children 將會變化,這時?getChildren上的 Watch 將會被調用,所以其它 Server 就知道已經有某臺 Server 死去了。新增 Server 也是同樣的原理。
?
Zookeeper 如何實現 Leader Election,也就是選出一個 Master Server。和前面的一樣每臺 Server 創建一個 EPHEMERAL 目錄節點,不同的是它還是一個 SEQUENTIAL 目錄節點,所以它是個 EPHEMERAL_SEQUENTIAL 目錄節點。之所以它是 EPHEMERAL_SEQUENTIAL 目錄節點,是因為我們可以給每臺 Server 編號,我們可以選擇當前是最小編號的 Server 為 Master,假如這個最小編號的 Server 死去,由于是 EPHEMERAL 節點,死去的 Server 對應的節點也被刪除,所以當前的節點列表中又出現一個最小編號的節點,我們就選擇這個節點為當前 Master。這樣就實現了動態選擇 Master,避免了傳統意義上單 Master 容易出現單點故障的問題。
?
圖 3. 集群管理結構圖
這部分的示例代碼如下,完整的代碼請看附件:
?
清單 3. Leader Election 關鍵代碼
?
void findLeader() throws InterruptedException { byte[] leader = null; try { leader = zk.getData(root + "/leader", true, null); } catch (Exception e) { logger.error(e); } if (leader != null) { following(); } else { String newLeader = null; try { byte[] localhost = InetAddress.getLocalHost().getAddress(); newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (Exception e) { logger.error(e); } if (newLeader != null) { leading(); } else { mutex.wait(); } } }?
?
-
共享鎖(Locks)
?
共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用?getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用?exists(String?path, boolean?watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。
?
圖 4. Zookeeper 實現 Locks 的流程圖
同步鎖的實現代碼如下,完整的代碼請看附件:
?
清單 4. 同步鎖的關鍵代碼
?
void getLock() throws KeeperException, InterruptedException{ List<String> list = zk.getChildren(root, false); String[] nodes = list.toArray(new String[list.size()]); Arrays.sort(nodes); if(myZnode.equals(root+"/"+nodes[0])){ doAction(); } else{ waitForLock(nodes[0]); } } void waitForLock(String lower) throws InterruptedException, KeeperException {Stat stat = zk.exists(root + "/" + lower,true); if(stat != null){ mutex.wait(); } else{ getLock(); } }?
?
-
隊列管理
?
Zookeeper 可以處理兩種類型的隊列:
?
?
同步隊列用 Zookeeper 實現的實現思路如下:
?
創建一個父目錄 /synchronizing,每個成員都監控標志(Set Watch)位目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創建 /synchronizing/member_i 的臨時目錄節點,然后每個成員獲取 / synchronizing 目錄的所有目錄節點,也就是 member_i。判斷 i 的值是否已經是成員的個數,如果小于成員個數等待 /synchronizing/start 的出現,如果已經相等就創建 /synchronizing/start。
?
用下面的流程圖更容易理解:
?
圖 5. 同步隊列流程圖
同步隊列的關鍵代碼如下,完整的代碼請看附件:
?
清單 5. 同步隊列
?
void addQueue() throws KeeperException, InterruptedException{ zk.exists(root + "/start",true); zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); synchronized (mutex) { List<String> list = zk.getChildren(root, false); if (list.size() < size) { mutex.wait(); } else { zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); } } }?
?
當隊列沒滿是進入 wait(),然后會一直等待 Watch 的通知,Watch 的代碼如下:
?
public void process(WatchedEvent event) { if(event.getPath().equals(root + "/start") &&event.getType() == Event.EventType.NodeCreated){ System.out.println("得到通知"); super.process(event); doAction(); } }?
?
FIFO 隊列用 Zookeeper 實現思路如下:
?
實現的思路也非常簡單,就是在特定的目錄下創建 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO。
?
下面是生產者和消費者這種隊列形式的示例代碼,完整的代碼請看附件:
?
清單 6. 生產者代碼
?
boolean produce(int i) throws KeeperException, InterruptedException{ ByteBuffer b = ByteBuffer.allocate(4); byte[] value; b.putInt(i); value = b.array(); zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); return true; }?
?
清單 7. 消費者代碼
?
int consume() throws KeeperException, InterruptedException{ int retvalue = -1; Stat stat = null; while (true) { synchronized (mutex) { List<String> list = zk.getChildren(root, true); if (list.size() == 0) { mutex.wait(); } else { Integer min = new Integer(list.get(0).substring(7)); for(String s : list){ Integer tempValue = new Integer(s.substring(7)); if(tempValue < min) min = tempValue; } byte[] b = zk.getData(root + "/element" + min,false, stat); zk.delete(root + "/element" + min, 0); ByteBuffer buffer = ByteBuffer.wrap(b); retvalue = buffer.getInt(); return retvalue; } } } }部分內容參考:http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/?
?
六.Zookeeper基本原理
1. 數據模型
如上圖所示,ZooKeeper數據模型的結構與Unix文件系統很類似,整體上可以看作是一棵樹,每個節點稱做一個ZNode。每個ZNode都可以通過其路徑唯一標識,比如上圖中第三層的第一個ZNode, 它的路徑是/app1/c1。在每個ZNode上可存儲少量數據(默認是1M, 可以通過配置修改, 通常不建議在ZNode上存儲大量的數據),這個特性非常有用,在后面的典型應用場景中會介紹到。另外,每個ZNode上還存儲了其Acl信息,這里需要注意,雖說ZNode的樹形結構跟Unix文件系統很類似,但是其Acl與Unix文件系統是完全不同的,每個ZNode的Acl的獨立的,子結點不會繼承父結點的。
2.重要概念?
2.1 ZNode
前文已介紹了ZNode, ZNode根據其本身的特性,可以分為下面兩類:
- Regular ZNode: 常規型ZNode, 用戶需要顯式的創建、刪除
- Ephemeral ZNode: 臨時型ZNode, 用戶創建它之后,可以顯式的刪除,也可以在創建它的Session結束后,由ZooKeeper Server自動刪除
ZNode還有一個Sequential的特性,如果創建的時候指定的話,該ZNode的名字后面會自動Append一個不斷增加的SequenceNo。
2.2 Session
Client與ZooKeeper之間的通信,需要創建一個Session,這個Session會有一個超時時間。因為ZooKeeper集群會把Client的Session信息持久化,所以在Session沒超時之前,Client與ZooKeeper Server的連接可以在各個ZooKeeper Server之間透明地移動。
在實際的應用中,如果Client與Server之間的通信足夠頻繁,Session的維護就不需要其它額外的消息了。否則,ZooKeeper Client會每t/3 ms發一次心跳給Server,如果Client 2t/3 ms沒收到來自Server的心跳回應,就會換到一個新的ZooKeeper Server上。這里t是用戶配置的Session的超時時間。
2.3 Watcher
ZooKeeper支持一種Watch操作,Client可以在某個ZNode上設置一個Watcher,來Watch該ZNode上的變化。如果該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,如果想繼續Watch的話,需要客戶端重新設置Watcher。這個跟epoll里的oneshot模式有點類似。
3. ZooKeeper特性?
3.1 讀、寫(更新)模式
在ZooKeeper集群中,讀可以從任意一個ZooKeeper Server讀,這一點是保證ZooKeeper比較好的讀性能的關鍵;寫的請求會先Forwarder到Leader,然后由Leader來通過ZooKeeper中的原子廣播協議,將請求廣播給所有的Follower,Leader收到一半以上的寫成功的Ack后,就認為該寫成功了,就會將該寫進行持久化,并告訴客戶端寫成功了。
3.2 WAL和Snapshot
和大多數分布式系統一樣,ZooKeeper也有WAL(Write-Ahead-Log),對于每一個更新操作,ZooKeeper都會先寫WAL, 然后再對內存中的數據做更新,然后向Client通知更新結果。另外,ZooKeeper還會定期將內存中的目錄樹進行Snapshot,落地到磁盤上,這個跟HDFS中的FSImage是比較類似的。這么做的主要目的,一當然是數據的持久化,二是加快重啟之后的恢復速度,如果全部通過Replay WAL的形式恢復的話,會比較慢。
3.3 FIFO
對于每一個ZooKeeper客戶端而言,所有的操作都是遵循FIFO順序的,這一特性是由下面兩個基本特性來保證的:一是ZooKeeper Client與Server之間的網絡通信是基于TCP,TCP保證了Client/Server之間傳輸包的順序;二是ZooKeeper Server執行客戶端請求也是嚴格按照FIFO順序的。
3.4 Linearizability
在ZooKeeper中,所有的更新操作都有嚴格的偏序關系,更新操作都是串行執行的,這一點是保證ZooKeeper功能正確性的關鍵。
ZooKeeper Client API
ZooKeeper Client Library提供了豐富直觀的API供用戶程序使用,下面是一些常用的API:
- create(path, data, flags): 創建一個ZNode, path是其路徑,data是要存儲在該ZNode上的數據,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
- delete(path, version): 刪除一個ZNode,可以通過version刪除指定的版本, 如果version是-1的話,表示刪除所有的版本
- exists(path, watch): 判斷指定ZNode是否存在,并設置是否Watch這個ZNode。這里如果要設置Watcher的話,Watcher是在創建ZooKeeper實例時指定的,如果要設置特定的Watcher的話,可以調用另一個重載版本的exists(path, watcher)。以下幾個帶watch參數的API也都類似
- getData(path, watch): 讀取指定ZNode上的數據,并設置是否watch這個ZNode
- setData(path, watch): 更新指定ZNode的數據,并設置是否Watch這個ZNode
- getChildren(path, watch): 獲取指定ZNode的所有子ZNode的名字,并設置是否Watch這個ZNode
- sync(path): 把所有在sync之前的更新操作都進行同步,達到每個請求都在半數以上的ZooKeeper Server上生效。path參數目前沒有用
- setAcl(path, acl): 設置指定ZNode的Acl信息
- getAcl(path): 獲取指定ZNode的Acl信息
ZooKeeper典型應用場景
1. 名字服務(NameService)?
分布式應用中,通常需要一套完備的命令機制,既能產生唯一的標識,又方便人識別和記憶。 我們知道,每個ZNode都可以由其路徑唯一標識,路徑本身也比較簡潔直觀,另外ZNode上還可以存儲少量數據,這些都是實現統一的NameService的基礎。下面以在HDFS中實現NameService為例,來說明實現NameService的基本布驟:
- 目標:通過簡單的名字來訪問指定的HDFS機群
- 定義命名規則:這里要做到簡潔易記憶。下面是一種可選的方案: [serviceScheme://][zkCluster]-[clusterName],比如hdfs://lgprc-example/表示基于lgprc ZooKeeper集群的用來做example的HDFS集群
- 配置DNS映射: 將zkCluster的標識lgprc通過DNS解析到對應的ZooKeeper集群的地址
- 創建ZNode: 在對應的ZooKeeper上創建/NameService/hdfs/lgprc-example結點,將HDFS的配置文件存儲于該結點下
- 用戶程序要訪問hdfs://lgprc-example/的HDFS集群,首先通過DNS找到lgprc的ZooKeeper機群的地址,然后在ZooKeeper的/NameService/hdfs/lgprc-example結點中讀取到HDFS的配置,進而根據得到的配置,得到HDFS的實際訪問入口
2. 配置管理(Configuration Management)?
在分布式系統中,常會遇到這樣的場景: 某個Job的很多個實例在運行,它們在運行時大多數配置項是相同的,如果想要統一改某個配置,一個個實例去改,是比較低效,也是比較容易出錯的方式。通過ZooKeeper可以很好的解決這樣的問題,下面的基本的步驟:
- 將公共的配置內容放到ZooKeeper中某個ZNode上,比如/service/common-conf
- 所有的實例在啟動時都會傳入ZooKeeper集群的入口地址,并且在運行過程中Watch /service/common-conf這個ZNode
- 如果集群管理員修改了了common-conf,所有的實例都會被通知到,根據收到的通知更新自己的配置,并繼續Watch /service/common-conf
3. 組員管理(Group Membership)?
在典型的Master-Slave結構的分布式系統中,Master需要作為“總管”來管理所有的Slave, 當有Slave加入,或者有Slave宕機,Master都需要感知到這個事情,然后作出對應的調整,以便不影響整個集群對外提供服務。以HBase為例,HMaster管理了所有的RegionServer,當有新的RegionServer加入的時候,HMaster需要分配一些Region到該RegionServer上去,讓其提供服務;當有RegionServer宕機時,HMaster需要將該RegionServer之前服務的Region都重新分配到當前正在提供服務的其它RegionServer上,以便不影響客戶端的正常訪問。下面是這種場景下使用ZooKeeper的基本步驟:
- Master在ZooKeeper上創建/service/slaves結點,并設置對該結點的Watcher
- 每個Slave在啟動成功后,創建唯一標識自己的臨時性(Ephemeral)結點/service/slaves/${slave_id},并將自己地址(ip/port)等相關信息寫入該結點
- Master收到有新子結點加入的通知后,做相應的處理
- 如果有Slave宕機,由于它所對應的結點是臨時性結點,在它的Session超時后,ZooKeeper會自動刪除該結點
- Master收到有子結點消失的通知,做相應的處理
4. 簡單互斥鎖(Simple Lock)?
我們知識,在傳統的應用程序中,線程、進程的同步,都可以通過操作系統提供的機制來完成。但是在分布式系統中,多個進程之間的同步,操作系統層面就無能為力了。這時候就需要像ZooKeeper這樣的分布式的協調(Coordination)服務來協助完成同步,下面是用ZooKeeper實現簡單的互斥鎖的步驟,這個可以和線程間同步的mutex做類比來理解:
- 多個進程嘗試去在指定的目錄下去創建一個臨時性(Ephemeral)結點 /locks/my_lock
- ZooKeeper能保證,只會有一個進程成功創建該結點,創建結點成功的進程就是搶到鎖的進程,假設該進程為A
- 其它進程都對/locks/my_lock進行Watch
- 當A進程不再需要鎖,可以顯式刪除/locks/my_lock釋放鎖;或者是A進程宕機后Session超時,ZooKeeper系統自動刪除/locks/my_lock結點釋放鎖。此時,其它進程就會收到ZooKeeper的通知,并嘗試去創建/locks/my_lock搶鎖,如此循環反復
5. 互斥鎖(Simple Lock without Herd Effect)?
上一節的例子中有一個問題,每次搶鎖都會有大量的進程去競爭,會造成羊群效應(Herd Effect),為了解決這個問題,我們可以通過下面的步驟來改進上述過程:
- 每個進程都在ZooKeeper上創建一個臨時的順序結點(Ephemeral Sequential) /locks/lock_${seq}
- ${seq}最小的為當前的持鎖者(${seq}是ZooKeeper生成的Sequenctial Number)
- 其它進程都對只watch比它次小的進程對應的結點,比如2 watch 1, 3 watch 2, 以此類推
- 當前持鎖者釋放鎖后,比它次大的進程就會收到ZooKeeper的通知,它成為新的持鎖者,如此循環反復
這里需要補充一點,通常在分布式系統中用ZooKeeper來做Leader Election(選主)就是通過上面的機制來實現的,這里的持鎖者就是當前的“主”。
6. 讀寫鎖(Read/Write Lock)?
我們知道,讀寫鎖跟互斥鎖相比不同的地方是,它分成了讀和寫兩種模式,多個讀可以并發執行,但寫和讀、寫都互斥,不能同時執行行。利用ZooKeeper,在上面的基礎上,稍做修改也可以實現傳統的讀寫鎖的語義,下面是基本的步驟:
- 每個進程都在ZooKeeper上創建一個臨時的順序結點(Ephemeral Sequential) /locks/lock_${seq}
- ${seq}最小的一個或多個結點為當前的持鎖者,多個是因為多個讀可以并發
- 需要寫鎖的進程,Watch比它次小的進程對應的結點
- 需要讀鎖的進程,Watch比它小的最后一個寫進程對應的結點
- 當前結點釋放鎖后,所有Watch該結點的進程都會被通知到,他們成為新的持鎖者,如此循環反復
7. 屏障(Barrier)?
在分布式系統中,屏障是這樣一種語義: 客戶端需要等待多個進程完成各自的任務,然后才能繼續往前進行下一步。下用是用ZooKeeper來實現屏障的基本步驟:
- Client在ZooKeeper上創建屏障結點/barrier/my_barrier,并啟動執行各個任務的進程
- Client通過exist()來Watch /barrier/my_barrier結點
- 每個任務進程在完成任務后,去檢查是否達到指定的條件,如果沒達到就啥也不做,如果達到了就把/barrier/my_barrier結點刪除
- Client收到/barrier/my_barrier被刪除的通知,屏障消失,繼續下一步任務
8. 雙屏障(Double Barrier)
雙屏障是這樣一種語義: 它可以用來同步一個任務的開始和結束,當有足夠多的進程進入屏障后,才開始執行任務;當所有的進程都執行完各自的任務后,屏障才撤銷。下面是用ZooKeeper來實現雙屏障的基本步驟:
- 進入屏障:
?
- Client Watch /barrier/ready結點, 通過判斷該結點是否存在來決定是否啟動任務
- 每個任務進程進入屏障時創建一個臨時結點/barrier/process/${process_id},然后檢查進入屏障的結點數是否達到指定的值,如果達到了指定的值,就創建一個/barrier/ready結點,否則繼續等待
- Client收到/barrier/ready創建的通知,就啟動任務執行過程
- 離開屏障:
?
- Client Watch /barrier/process,如果其沒有子結點,就可以認為任務執行結束,可以離開屏障?
- 每個任務進程執行任務結束后,都需要刪除自己對應的結點/barrier/process/${process_id}?
?
綜上,Zookeeper 的更多知識,在深入學習和使用之后,再做補充。
?
轉載于:https://www.cnblogs.com/haochuang/p/5133827.html
總結
以上是生活随笔為你收集整理的Zookeeper相关知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux系统备份还原工具1(DD)
- 下一篇: 【果断收藏】原来SEO站外推广可以推进营