摘要:可靠性一旦數據更新成功,將一直保持,直到新的更新。這是一種主動的分布式數據結構,能夠在外部情況發生變化時候主動修改數據項狀態的數據機構。如果監視節點狀態發生變化,則跳轉到第步,繼續進行后續的操作,直到退出鎖競爭。
題外話:從字面上來看,ZooKeeper表示動物園管理員,而Hadoop生態系統中,許多項目的Logo都采用了動物,比如Hadoop采用了大象的形象,所以可以ZooKeeper就是對這些動物進行一些管理工作的。
對于單機環境進程內的協調方法,我們一般通過線程鎖來協調對共享數據的訪問以保證狀態的一致性。 但是分布式環境如何進行協調呢?于是,Google創造了Chubby,而ZooKeeper則是對于Chubby的一個開源實現。
ZooKeeper是一種為分布式應用所設計的高可用、高性能且一致的開源協調服務,它提供了一項基本服務:分布式鎖服務。由于ZooKeeper的開源特性,后來我們的開發者在分布式鎖的基礎上,摸索了出了其他的使用方法:配置維護、組服務、分布式消息隊列、分布式通知/協調等。它被設計為易于編程,使用文件系統目錄樹作為數據模型。
Zookeeper服務自身組成一個集群(2n+1個服務允許n個失效)。Zookeeper服務有兩個角色,一個是leader,負責寫服務和數據同步,剩下的是follower,提供讀服務,leader失效后會在follower中重新選舉新的leader。
保證
順序一致性:按照客戶端發送請求的順序更新數據。
原子性:更新要么成功,要么失敗,不會出現部分更新。
單一性 :無論客戶端連接哪個server,都會看到同一個視圖。
可靠性:一旦數據更新成功,將一直保持,直到新的更新。
及時性:客戶端會在一個確定的時間內得到最新的數據。
1、Zookeeper數據模型Znode
Zookeeper表現為一個分層的文件系統目錄樹結構(不同于文件系統的是,節點可以有自己的數據,而文件系統中的目錄節點只有子節點)。
Zookeeper Stat 結構 —— Zookeeper中的每個znode的stat都由下面的字段組成:
czxid - 引起這個znode創建的zxid
mzxid - znode最后更新的zxid
ctime - znode被創建的毫秒數(從1970年開始)
mtime - znode最后修改的毫秒數(從1970年開始)
version - znode數據變化號
cversion - znode子節點變化號
aversion - znode訪問控制列表的變化號
ephemeralOwner - 如果是臨時節點這個是znode擁有者的session id。如果不是臨時節點則是0。
dataLength - znode的數據長度
numChildren - znode子節點數量
關于數據模型的理解,建議參考:http://www.cnblogs.com/wuxl36...
簡單API——Zookeeper的設計目標的其中之一就是提供一個簡單的程序接口。因此,它只支持這些操作:
create - 在樹形結構的位置中創建節點
delete - 刪除一個節點
exists - 測試節點在指定位置上是否存在
get data - 從節點上讀取數據
set data - 往節點寫入數據
get chilren - 檢索節點的子節點列表
sync - 等待傳輸數據
ZooKeeper的應用場景(1)統一命名服務
分布式應用中,通常需要有一套完整的命名規則,既能夠產生唯一的名稱又便于人識別和記住,通常情況下用樹形的名稱結構是一個理想的選擇,樹形的名稱結構是一個有層次的目錄結構,既對人友好又不會重復。說到這里你可能想到了 JNDI,沒錯 Zookeeper 的 Name Service 與 JNDI 能夠完成的功能是差不多的,它們都是將有層次的目錄結構關聯到一定資源上,但是 Zookeeper 的 Name Service 更加是廣泛意義上的關聯,也許你并不需要將名稱關聯到特定資源上,你可能只需要一個不會重復名稱,就像數據庫中產生一個唯一的數字主鍵一樣。
Name Service 已經是 Zookeeper 內置的功能,你只要調用 Zookeeper 的 API 就能實現。如調用 create 接口就可以很容易創建一個目錄節點。
案例:有一組服務器向客戶端提供某種服務(例如:使用LVS技術構建的Web網站集群,就是由N臺服務器組成的集群,為用戶提供Web服務)。對于這種場景,我們的程序中一定有一份這組服務器的列表,每次客戶端請求時候,都是從這份列表里讀取這份服務器列表。那么這分列表顯然不能存儲在一臺單節點的服務器上,否則這個節點掛掉了,整個集群都會發生故障,我們希望這份列表時高可用的。高可用的解決方案是:這份列表是分布式存儲的,它是由存儲這份列表的服務器共同管理的,如果存儲列表里的某臺服務器壞掉了,其他服務器馬上可以替代壞掉的服務器,并且可以把壞掉的服務器從列表里刪除掉,讓故障服務器退出整個集群的運行,而這一切的操作又不會由故障的服務器來操作,而是集群里正常的服務器來完成。這是一種主動的分布式數據結構,能夠在外部情況發生變化時候主動修改數據項狀態的數據機構。
(2)分布式鎖服務
共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。
具體步驟如下:
加鎖: ZooKeeper 將按照如下方式實現加鎖的操作:
1 ) ZooKeeper 調用 create ()方法來創建一個路徑格式為“ _locknode_/lock- ”的節點,此節點類型為sequence (連續)和 ephemeral (臨時)。也就是說,創建的節點為臨時節點,并且所有的節點連續編號,即“ lock-i ”的格式。
2 )在創建的鎖節點上調用 getChildren ()方法,來獲取鎖目錄下的最小編號節點,并且不設置 watch 。
3 )步驟 2 中獲取的節點恰好是步驟 1 中客戶端創建的節點,那么此客戶端獲得此種類型的鎖,然后退出操作。
4 )客戶端在鎖目錄上調用 exists ()方法,并且設置 watch 來監視鎖目錄下比自己小一個的連續臨時節點的狀態。
5 )如果監視節點狀態發生變化,則跳轉到第 2 步,繼續進行后續的操作,直到退出鎖競爭。
解鎖:
ZooKeeper 解鎖操作非常簡單,客戶端只需要將加鎖操作步驟 1 中創建的臨時節點刪除即可。
void getLock() throws KeeperException, InterruptedException{ Listlist = 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(); } }
(3)配置管理(數據發布與訂閱)
在分布式系統里,我們會把一個服務應用分別部署到n臺服務器上,這些服務器的配置文件是相同的,如果配置文件的配置選項發生變化,那么我們就得一個個去改這些配置文件,如果我們需要改的服務器比較少,這些操作還不是太麻煩,如果我們分布式的服務器特別多,那么更改配置選項就是一件麻煩而且危險的事情。這時我們可以將配置信息保存在 Zookeeper 的某個目錄節點中,然后將所有需要修改的應用機器監控配置信息的狀態,一旦配置信息發生變化,每臺應用機器就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應用到系統中。
(4)集群管理
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 容易出現單點故障的問題。
PS:關于Master的選舉,可以參考:http://www.cnblogs.com/sundde...。
注意:ZooKeeper所提供的服務主要是通過:數據結構+原語(一些關于該數據結構的一些操作)+watcher機制,三個部分來實現的
(5)、隊列管理
Zookeeper 可以處理兩種類型的隊列:
當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達,這種是同步隊列。
隊列按照 FIFO 方式進行入隊和出隊操作,例如實現生產者和消費者模型。
A、同步隊列 用 Zookeeper 實現的實現思路如下:
創建一個父目錄 /synchronizing,每個成員都監控標志(Set Watch)位目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創建 /synchronizing/member_i 的臨時目錄節點,然后每個成員獲取 / synchronizing 目錄的所有目錄節點,也就是 member_i。判斷 i 的值是否已經是成員的個數,如果小于成員個數等待 /synchronizing/start 的出現,如果已經相等就創建 /synchronizing/start。
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) { Listlist = zk.getChildren(root, false); if (list.size() < size) { mutex.wait(); } else { zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } public void process(WatchedEvent event) { if(event.getPath().equals(root + "/start") && event.getType() == Event.EventType.NodeCreated){ System.out.println("得到通知"); super.process(event); doAction(); } }
B、FIFO隊列:
實現的思路也非常簡單,就是在特定的目錄下創建 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO。
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; } int consume() throws KeeperException, InterruptedException{ int retvalue = -1; Stat stat = null; while (true) { synchronized (mutex) { ListZooKeeper集群模式搭建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; } } } }
注意:ZooKeeper服務器集群規模不小于3個節點,要求各服務器之間系統時間要保持一致;
1、下載解壓安裝后,修改環境變量:vim /etc/profile
增加一行:export ZOOKEEPER_HOME=/usr/local/zookeeper
修改PATH:export PATH=.:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin:$PATH
使配置生效:source /etc/profile
2、進入zookeeper的conf目錄下,修改文件名:mv zoo_sample.cfg zoo.cfg
編輯zoo.cfg:vim zoo.cfg
修改dataDir=/usr/local/zookeeper/data
新增
server.0=hadoop-master:2888:3888
server.1=hadoop-slave1:2888:3888
server.2=hadoop-slave2:2888:3888
3、創建data文件夾,并創建myid文件:
新建data文件夾:mkdir /usr/local/zookeeper/data
新建myid文件:vim myid,并設置第一臺server為0。
4、復制zookeeper目錄至其余兩臺服務器中:
scp /usr/local/zookeeper hadoop-slave1:/usr/local/
scp /usr/local/zookeeper hadoop-slave2:/usr/local/
5、復制環境變量配置文件至其余兩臺服務器中:
scp /etc/profile hadoop-slave1:/etc
scp /etc/profile hadoop-slave2:/etc
6、在其余兩臺服務器中修改myid文件:設置為1和2;
7、啟動ZooKeeper,分別在三個節點中執行命令:zkServer.sh start
8、檢驗ZooKeeper集群節點角色狀態,分別在三個節點中執行命令:zkServer.sh status (可以看出哪個節點是leader,follower,observer等)
ZooKeeper中包含以下角色:
領導者(leader),負責進行投票的發起和決議,更新系統狀態;
學習者(learner),包括跟隨者(follower)和觀察者(observer),follower用于接受客戶端請求并向客戶端返回結果,在選主過程中參與投票;observer可以接受客戶端連接,將寫請求轉發給leader,但observer不參加投票過程,只同步leader的狀態,observer的目的是為了擴展系統,提高讀取速度;
9、ZooKeeper簡單測試
搭建好集群環境后,就可以進行簡單的讀寫一致性測試了,這里我們通過進入zookeeper的bin目錄下的zkCli.sh來完成下面的操作:
(1)在其中一個節點192.168.80.100上執行一個寫操作:create /MyTest test
(2)在其他兩個節點上執行讀操作:get /MyTest
TIP:可以在一個節點中通過zkCli.sh -server hadoop-slave1:2181來遠程登錄
(3)在其中一個節點192.168.80.101上執行一個修改操作:set /MyTest new-test ,在其他兩個節點上執行讀操作查看數據是否一致
[zkshell: 0] help ZooKeeper host:port cmd args get path [watch] ls path [watch] set path data [version] delquota [-n|-b] path quit printwatches on|off createpath data acl stat path [watch] listquota path history setAcl path acl getAcl path sync path redo cmdno addauth scheme auth delete path [version] setquota -n|-b val path編程
APIDOC: https://zookeeper.apache.org/...
org.apache.zookeeper zookeeper 3.4.6
Zookeeper主要用來解決分布式集群中應用系統的一致性問題,它能提供基于類似于文件系統的目錄節點樹方式的數據存儲,但是 Zookeeper 并不是用來專門存儲數據的,它的作用主要是用來維護和監控你存儲的數據的狀態變化。通過監控這些數據狀態的變化,從而可以達到基于數據的集群管理
客戶端要連接 Zookeeper 服務器可以通過創建 org.apache.zookeeper.ZooKeeper 的一個實例對象,然后調用這個類提供的接口來和服務器交互。
前面說了 ZooKeeper 主要是用來維護和監控一個目錄節點樹中存儲的數據的狀態,所有我們能夠操作 ZooKeeper 的也和操作目錄節點樹大體一樣,如創建一個目錄節點,給某個目錄節點設置數據,獲取某個目錄節點的所有子目錄節點,給某個目錄節點設置權限和監控這個目錄節點的狀態變化。
常見方法:
String create(String path, byte[] data, List
CreateMode 標識有四種形式的目錄節點:
PERSISTENT:持久化目錄節點,這個目錄節點存儲的數據不會丟失;
PERSISTENT_SEQUENTIAL:順序自動編號的目錄節點,這種目錄節點會根據當前已近存在的節點數自動加 1,然后返回給客戶端已經成功創建的目錄節點名;
EPHEMERAL:臨時目錄節點,一旦創建這個節點的客戶端與服務器端口也就是 session 超時,這種節點會被自動刪除;
EPHEMERAL_SEQUENTIAL:臨時自動編號節點
Stat exists(String path, boolean watch) 判斷某個 path 是否存在,并設置是否監控這個目錄節點,這里的 watcher 是在創建 ZooKeeper 實例時指定的 watcher
Stat exists(String path,Watcher watcher) 重載方法,這里給某個目錄節點設置特定的 watcher,Watcher 在 ZooKeeper 是一個核心功能,Watcher 可以監控目錄節點的數據變化以及子目錄的變化,一旦這些狀態發生變化,服務器就會通知所有設置在這個目錄節點上的 Watcher,從而每個客戶端都很快知道它所關注的目錄節點的狀態發生變化,而做出相應的反應
void delete(String path, int version) 刪除 path 對應的目錄節點,version 為 -1 可以匹配任何版本,也就刪除了這個目錄節點所有數據
List
Stat setData(String path, byte[] data, int version) 給 path 設置數據,可以指定這個數據的版本號,如果 version 為 -1 怎可以匹配任何版本
byte[] getData(String path, boolean watch, Stat stat) 獲取這個 path 對應的目錄節點存儲的數據,數據的版本等信息可以通過 stat 來指定,同時還可以設置是否監控這個目錄節點數據的狀態
void addAuthInfo(String scheme, byte[] auth) 客戶端將自己的授權信息提交給服務器,服務器將根據這個授權信息驗證客戶端的訪問權限。
Stat setACL(String path,List
Perms 有 ALL、READ、WRITE、CREATE、DELETE、ADMIN 幾種,而 id 標識了訪問目錄節點的身份列表,默認情況下有以下兩種:ANYONE_ID_UNSAFE = new Id("world", "anyone") 和 AUTH_IDS = new Id("auth", "") 分別表示任何人都可以訪問和創建者擁有訪問權限。
List
// 創建一個與服務器的連接 ZooKeeper zk = new ZooKeeper("localhost:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, new Watcher() { // 監控所有被觸發的事件 public void process(WatchedEvent event) { System.out.println("已經觸發了" + event.getType() + "事件!"); } }); // 創建一個目錄節點 zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // 創建一個子目錄節點 zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData("/testRootPath",false,null))); // 取出子目錄節點列表 System.out.println(zk.getChildren("/testRootPath",true)); // 修改子目錄節點數據 zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1); System.out.println("目錄節點狀態:["+zk.exists("/testRootPath",true)+"]"); // 創建另外一個子目錄節點 zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null))); // 刪除子目錄節點 zk.delete("/testRootPath/testChildPathTwo",-1); zk.delete("/testRootPath/testChildPathOne",-1); // 刪除父目錄節點 zk.delete("/testRootPath",-1); // 關閉連接 zk.close();
官網例子請看:http://zookeeper.majunwei.com...
其他例子:
http://www.uml.org.cn/zjjs/20...
http://www.uml.org.cn/zjjs/20...
http://zookeeper.majunwei.com...
參考:官方文檔:https://zookeeper.apache.org/...
小馬過河翻譯社,Zookeeper文檔中文版:http://zookeeper.majunwei.com...
周旭龍,ZooKeeper環境搭建:http://www.cnblogs.com/edison...
Zookeeper 的學習與運用:http://www.oschina.net/questi...
鄔興亮,Zookeeper隨筆系列:http://www.cnblogs.com/wuxl36...
ggjucheng,Zookeeper Api(java)入門與應用:http://www.cnblogs.com/ggjuch...
其他推薦:
http://cailin.iteye.com/blog/...
http://www.uml.org.cn/wenzhan...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67377.html
摘要:相關概念協議高級消息隊列協議是一個標準開放的應用層的消息中間件協議。可以用命令與不同,不是線程安全的。手動提交執行相關邏輯提交注意點將寫成單例模式,有助于減少端占用的資源。自身是線程安全的類,只要封裝得當就能最恰當的發揮好的作用。 本文使用的Kafka版本0.11 先思考些問題: 我想分析一下用戶行為(pageviews),以便我能設計出更好的廣告位 我想對用戶的搜索關鍵詞進行統計,...
摘要:在的的首次會議慶祝了其新功能版本的發布。可以以這三種方式暴露出來內部外部和負載均衡。比如,可能會通過一個彈性負載均衡器接收流量,或者通過谷歌的負載均衡器接收。這個功能令第三方負載均衡器整合到。負起了接管發現任務和微服務負載均衡器的重任。 隨著容器逐漸受到企業的注意,焦點慢慢被轉移到了容器編排工具上。復雜的工作負載在生產過程中需要成熟地被調度,編排,彈性擴容和管理工具。有了Docker,...
摘要:同時集成了機器學習類庫。基于計算框架,將的分布式計算應用到機器學習領域。提供了一個簡單的聲明方法指定機器學習任務,并且動態地選擇最優的學習算法。宣稱其性能是的多倍。 介紹 spark是分布式并行數據處理框架 與mapreduce的區別: mapreduce通常將中間結果放在hdfs上,spark是基于內存并行大數據框架,中間結果放在內存,對于迭代數據spark效率更高,mapred...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 982·2023-04-26 01:47
閱讀 1672·2021-11-18 13:19
閱讀 2042·2019-08-30 15:44
閱讀 645·2019-08-30 15:44
閱讀 2291·2019-08-30 15:44
閱讀 1232·2019-08-30 14:06
閱讀 1420·2019-08-30 12:59
閱讀 1900·2019-08-29 12:49