1. 程式人生 > >ZooKeeper學習第六期---ZooKeeper機制架構

ZooKeeper學習第六期---ZooKeeper機制架構

一、ZooKeeper許可權管理機制 

1.1 許可權管理ACL(Access Control List) 

ZooKeeper 的許可權管理亦即ACL 控制功能,使用ACL來對Znode進行訪問控制。ACL的實現和Unix檔案訪問許可非常相似:它使用許可位來對一個節點的不同操作進行允許或禁止的權 限控制。但是和標準的Unix許可不同的是,Zookeeper對於使用者類別的區分,不止侷限於所有者(owner)、組 (group)、所有人(world)三個級別。Zookeeper中,資料節點沒有"所有者"的概念。訪問者利用id標識自己的身份,並獲得與之相應的 不同的訪問許可權。

ZooKeeper 的許可權管理通過Server、Client 兩端協調完成:

(1) Server端

一個ZooKeeper 的節點儲存兩部分內容:資料狀態,狀態中包含ACL 資訊。建立一個znode 會產生一個ACL 列表,列表中每個ACL 包括:

 許可權perms

 驗證模式scheme

 具體內容expression:Ids

例如,當scheme="digest" 時, Ids 為使用者名稱密碼, 即"root :J0sTy9BCUKubtK1y8pkbL7qoxSw"。ZooKeeper 提供瞭如下幾種驗證模式:

① 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。

注意的是,exists操作和getAcl操作並不受ACL許可控制,因此任何客戶端可以查詢節點的狀態和節點的ACL。

(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.2 ZooKeeper SuperDigest

(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。

二、 Watch機制

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伺服器。由於當連線斷開時收不到"監視",所以在這種情況下,模組行為需要容錯方面的設計。

三、Session機制

3.1 會話概述

每個ZooKeeper客戶端的配置中都包括集合體中伺服器的列表。在啟動時,客戶端會嘗試連線到列表中的一臺伺服器。如果連線失敗,它會嘗試連線另一臺伺服器,以此類推,直到成功與一臺伺服器建立連線或因為所有ZooKeeper伺服器都不可用而失敗。

圖 3.1 ZooKeeper體系結構

一旦客戶端與一臺ZooKeeper伺服器建立連線,這臺伺服器就會為該客戶端建立一個新的會話。每個會話都會有一個超時的時間設定,這個設定由建立會話的應用來設定。如果伺服器在超時時間段內沒有收到任何請求,則相應的會話會過期。一旦一個會話已經過期,就無法重新開啟,並且任何與該會話相關聯的短暫znode都會丟失。會話通常長期存在,而且會話過期是一種比較罕見的事件,但對應用來說,如何處理會話過期仍是非常重要的。

只要一個會話空閒超過一定時間,都可以通過客戶端傳送ping請求(也稱為心跳)保持會話不過期。ping請求由ZooKeeper的客戶端庫自動傳送,因此在我們的程式碼中不需要考慮如何維護會話。這個時間長度的設定應當足夠低,以便能檔檢測出伺服器故障(由讀超時體現),並且能夠在會話超時的時間段內重新蓮接到另外一臺伺服器。

3.2 故障切換

ZooKeeper客戶端可以自動地進行故障切換,切換至另一臺ZooKeeper伺服器。並且關鍵的一點是,在另一臺伺服器接替故障伺服器之後,所有的會話和相關的短暫Znode仍然是有效的。在故障切換過程中,應用程式將收到斷開連線和連線至服務的通知。當客戶端斷開連線時,觀察通知將無法傳送;但是當客戶端成功恢復連線後,這些延遲的通知會被髮送。當然,在客戶端重新連線至另一臺伺服器的過程中,如果應用程式試圖執行一個操作,這個操作將會失敗。這充分體現了在真實的ZooKeeper應用中處理連線丟失異常的重要性。

四、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服務。