ZooKeeper 的權限管理亦即ACL 控制功能,使用ACL來對Znode進行訪問控制。ACL的實現和Unix文件訪問許可很是類似:它使用許可位來對一個節點的不一樣操做進行容許或禁止的權 限控制。可是和標準的Unix許可不一樣的是,Zookeeper對於用戶類別的區分,不止侷限於全部者(owner)、組 (group)、全部人(world)三個級別。Zookeeper中,數據節點沒有"全部者"的概念。訪問者利用id標識本身的身份,並得到與之相應的 不一樣的訪問權限。java
ZooKeeper 的權限管理經過Server、Client 兩端協調完成:node
(1) Server端算法
一個ZooKeeper 的節點存儲兩部份內容:數據和狀態,狀態中包含ACL 信息。建立一個znode 會產生一個ACL 列表,列表中每一個ACL 包括:express
① 權限permsapache
② 驗證模式scheme服務器
③ 具體內容expression:Idssession
例如,當scheme="digest" 時, Ids 爲用戶名密碼, 即"root :J0sTy9BCUKubtK1y8pkbL7qoxSw"。ZooKeeper 提供了以下幾種驗證模式:ide
① Digest: Client 端由用戶名和密碼驗證,譬如user:pwd函數
② Host: Client 端由主機名驗證,譬如localhost測試
③ Ip:Client 端由IP 地址驗證,譬如172.2.0.0/24
④ World :固定用戶爲anyone,爲全部Client 端開放權限
當會話創建的時候,客戶端將會進行自我驗證。
權限許可集合以下:
① Create 容許對子節點Create 操做
② Read 容許對本節點GetChildren 和GetData 操做
③ Write 容許對本節點SetData 操做
④ Delete 容許對子節點Delete 操做
⑤ Admin 容許對本節點setAcl 操做
另外,ZooKeeper Java API支持三種標準的用戶權限,它們分別爲:
① ZOO_PEN_ACL_UNSAFE:對於全部的ACL來講都是徹底開放的,任何應用程序能夠在節點上執行任何操做,好比建立、列出並刪除子節點。
② ZOO_READ_ACL_UNSAFE:對於任意的應用程序來講,僅僅具備讀權限。
③ ZOO_CREATOR_ALL_ACL:授予節點建立者全部權限。須要注意的是,設置此權限以前,建立者必須已經通了服務器的認證。
Znode ACL 權限用一個int 型數字perms 表示,perms 的5 個二進制位分別表示setacl、delete、create、write、read。好比adcwr=0x1f,----r=0x1,a-c-r=0x15。
(2) 客戶端
Client 經過調用addAuthInfo()函數設置當前會話的Author信息(針對Digest 驗證模式)。Server 收到Client 發送的操做請求(除exists、getAcl 以外),須要進行ACL 驗證:對該請求攜帶的Author 明文信息加密,並與目標節點的ACL 信息進行比較,若是匹配則具備相應的權限,不然請求被Server 拒絕。
下面演示一個經過digest(用戶名:密碼的方式)爲建立的節點設置ACL的例子,代碼以下:
import org.apache.Zookeeper.*; import org.apache.Zookeeper.server.auth.DigestAuthenticationProvider; import org.apache.Zookeeper.data.*; import java.util.*; public class NewDigest { public static void main(String[] args) throws Exception {//new一個acl List<ACL> acls = new ArrayList<ACL>(); //添加第一個id,採用用戶名密碼形式 Id id1 = new Id("digest",DigestAuthenticationProvider.generateDigest("admin:admin")); ACL acl1 = new ACL(ZooDefs.Perms.ALL, id1); acls.add(acl1); //添加第二個id,全部用戶可讀權限 Id id2 = new Id("world", "anyone"); ACL acl2 = new ACL(ZooDefs.Perms.READ, id2); acls.add(acl2); // Zk用admin認證,建立/test ZNode。 ZooKeeper Zk = new ZooKeeper("host1:2181,host2:2181,host3:2181",2000, null); Zk.addAuthInfo("digest", "admin:admin".getBytes()); Zk.create("/test", "data".getBytes(), acls, CreateMode.PERSISTENT); } }
(1) 一次Client 對Znode 進行操做的驗證ACL 的方式爲:
a) 遍歷znode的全部ACL:
① 對於每個ACL,首先操做類型與權限(perms)匹配
② 只有匹配權限成功才進行session 的auth 信息與ACL 的用戶名、密碼匹配
b) 若是兩次匹配都成功,則容許操做;不然,返回權限不夠error(rc=-102)
(2) 若是Znode ACL List 中任何一個ACL 都沒有setAcl 權限,那麼就算superDigest 也修改不了它的權限;再假如這個Znode 還不開放delete 權限,那麼它的全部子節點都將不會被刪除。惟一的辦法是經過手動刪除snapshot 和log 的方法,將ZK 回滾到一個之前的狀態,而後重啓,固然這會影響到該znode 之外其它節點的正常應用。
(3) superDigest 設置的步驟:
① 啓動ZK 的時候( zkServer.sh ) , 加入參數: Java"-Dzookeeper .DigestAuthenticationProvider.superDigest=super:D/InIHSb7yEEbrWz8b9l71RjZJU=" (無空格)。
② 在客戶端使用的時候, addAuthInfo("digest", "super:test", 10, 0, 0); " super:test" 爲"super:D/InIHSb7yEEbrWz8b9l71RjZJU="的明文表示,加密算法同setAcl。
Zookeeper客戶端在數據節點上設置監視,則當數據節點發生變化時,客戶端會收到提醒。ZooKeeper中的各類讀請求,如getDate(),getChildren(),和exists(),均可以選擇加"監視點"(watch)。"監視點"指的是一種一次性的觸發器(trigger),當受監視的數據發生變化時,該觸發器會通知客戶端。
(1) 監視機制有三個關鍵點:
① "監視點"是一次性的,當觸發過一次以後,除非從新設置,新的數據變化不會提醒客戶端。
② "監視點"將數據改變的通知客戶端。若是數據改變是客戶端A引發的,不能保證"監視點"通知事件會在引起數據修改的函數返回前到達客戶端A。
③ 對於"監視點",ZooKeeper有以下保證:客戶端必定是在接收到"監視"事件(watch event)以後才接收到數據的改變信息。
(2) "監視點"保留在ZooKeeper服務器上,則當客戶端鏈接到新的ZooKeeper服務器上時,全部須要被觸發的相關"監視點"都會被觸發。當客戶端斷線後重連,與它的相關的"監視點"都會自動從新註冊,這對客戶端來講是透明的。在如下狀況,"監視點"會被錯過:客戶端B設置了關於節點A存在性的"監視點",但B斷線了,在B斷線過程當中節點A被建立又被刪除。此時,B再連線後不知道A節點曾經被建立過。
(3) ZooKeeper的"監視"機制保證如下幾點:
① "監視"事件的觸發順序和事件的分發順序一致。
② 客戶端將先接收到"監視"事件,而後才收到新的數據
③ "監視"事件觸發的順序與ZooKeeper服務器上數據變化的順序一致
(4) 關於ZooKeeper"監視"機制的注意點:
① "監視點"是一次性的。
② 因爲"監視點"是一次性的,並且,從接收到"監視"事件到設置新"監視點"是有延時的,因此客戶端可能監控不到數據的全部變化。
③ 一個監控對象,只會被相關的通知觸發一次。若是一個客戶端設置了關於某個數據點exists和getData的監控,則當該數據被刪除的時候,只會觸發"文件被刪除"的
通知。
④ 當客戶端斷開與服務器的鏈接時,客戶端再也不能收到"監視"事件,直到從新得到鏈接。因此關於Session的信息將被髮送給全部ZooKeeper服務器。因爲當鏈接斷開時收不到"監視",因此在這種狀況下,模塊行爲須要容錯方面的設計。
每一個ZooKeeper客戶端的配置中都包括集合體中服務器的列表。在啓動時,客戶端會嘗試鏈接到列表中的一臺服務器。若是鏈接失敗,它會嘗試鏈接另外一臺服務器,以此類推,直到成功與一臺服務器創建鏈接或由於全部ZooKeeper服務器都不可用而失敗。
圖 3.1 ZooKeeper體系結構
一旦客戶端與一臺ZooKeeper服務器創建鏈接,這臺服務器就會爲該客戶端建立一個新的會話。每一個會話都會有一個超時的時間設置,這個設置由建立會話的應用來設定。若是服務器在超時時間段內沒有收到任何請求,則相應的會話會過時。一旦一個會話已通過期,就沒法從新打開,而且任何與該會話相關聯的短暫znode都會丟失。會話一般長期存在,並且會話過時是一種比較罕見的事件,但對應用來講,如何處理會話過時還是很是重要的。
只要一個會話空閒超過必定時間,均可以經過客戶端發送ping請求(也稱爲心跳)保持會話不過時。ping請求由ZooKeeper的客戶端庫自動發送,所以在咱們的代碼中不須要考慮如何維護會話。這個時間長度的設置應當足夠低,以便能檔檢測出服務器故障(由讀超時體現),而且可以在會話超時的時間段內從新蓮接到另一臺服務器。
ZooKeeper客戶端能夠自動地進行故障切換,切換至另外一臺ZooKeeper服務器。而且關鍵的一點是,在另外一臺服務器接替故障服務器以後,全部的會話和相關的短暫Znode仍然是有效的。在故障切換過程當中,應用程序將收到斷開鏈接和鏈接至服務的通知。當客戶端斷開鏈接時,觀察通知將沒法發送;可是當客戶端成功恢復鏈接後,這些延遲的通知會被髮送。固然,在客戶端從新鏈接至另外一臺服務器的過程當中,若是應用程序試圖執行一個操做,這個操做將會失敗。這充分體現了在真實的ZooKeeper應用中處理鏈接丟失異常的重要性。
(1) ZooKeeper狀態
ZooKeeper對象在其生命週期中會經歷幾種不一樣的狀態。你能夠在任什麼時候刻經過getState()方法來查詢對象的狀態:
public States getState()
States被定義成表明ZooKeeper對象不一樣狀態的枚舉類型值(不論是什麼枚舉值,一個ZooKeeper的實例在一個時刻只能處於一種狀態)。在試圖與ZooKeeper服務創建鏈接的過程當中,一個新建的ZooKeeper實例處於CONNECTING狀態。一旦創建鏈接,它就會進入CONNECTED狀態。
圖 3.2 ZooKeeper狀態轉換
經過註冊觀察對象,使用了ZooKeeper對象的客戶端能夠收到狀態轉換通知。在進入CONNECTED狀態時,觀察對象會收到一個WatchedEvent通知,其中KeeperState的值是SyncConnected。
(2) Watch與ZooKeeper狀態
ZooKeeper的觀察對象肩負着雙重責任:
① 能夠用來得到ZooKeeper狀態變化的相關通知;
② 能夠用來得到Znode變化的相關通知。
監視ZooKeeper狀態變化:可使用ZooKeeper對象默認構造函數的觀察。
監視Znode變化:可使用一個專用的觀察對象,將其傳遞給適當的讀操做。也能夠經過讀操做中的布爾標識來設定是否共享使用默認的觀察。
ZooKeeper實例可能失去或從新鏈接ZooKeeper服務,在CONNECTED和CONNECTING狀態中切換。若是鏈接斷 開,watcher獲得一個Disconnected事件。學要注意的是,這些狀態的遷移是由ZooKeeper實例本身發起的,若是鏈接斷開他將自動嘗 試自動鏈接。
若是任何一個close()方法被調用,或是會話由Expired類型的KeepState提示過時時,ZooKeeper可能會轉變成第三種狀態 CLOSED。一旦處於CLOSED狀態,ZooKeeper對象將再也不是活動的了(可使用states的isActive()方法進行測試),並且不 能被重用。客戶端必須創建一個新的ZooKeeper實例才能從新鏈接到ZooKeeper服務。