Chapter 6 ZooKeeper 注意事項 —— 筆記

1、使用 ACL

每次建立znode節點時,必須設置訪問權限,並且子節點並不會繼承父節點的訪問權限。訪問權限的檢查也是基於每個 znode 節點的,若是一個客戶端能夠訪問一個 znode 節點,即便這個客戶端無權訪問該節點的父節點,仍然能夠訪問這個 znode 節點。java

ZooKeeper 經過訪問控制表(ACL)來控制訪問權限。一個ACL包括如下形式的記錄:scheme:auth-infonode

  • scheme:對應了一組內置的鑑權模式
  • auth-info:爲對於特定模式所對應的方式進行編碼的鑑權信息
ZooKeeper經過檢查客戶端進程訪問每一個節點時提交上來的受權信息來保證安全性。若是一個進程沒有提供鑑權信息,或者鑑權信息與要請求的znode節點的信息不匹配,進程就會收到一個權限錯誤。

爲了給一個ZooKeeper增長鑑權信息,須要調用 addAuthInfo 方法,形式以下:apache

void addAuthInfo(
    String scheme,
    byte auth[]
)
  • scheme:表示所採用的鑑權模式。
  • auth:表示發送給服務器的鑑權信息。該參數的類型爲byte[]類型,不過大部分的鑑權模式須要一個String類型的信息,因此你能夠經過String.getBytes()來將String轉換爲byte[]。
一個進程能夠在任什麼時候候調用addAuthInfo來添加鑑權信息。通常狀況下,在ZooKeeper句柄建立後就會調用該方法來添加鑑權信息。進程中能夠屢次調用該方法,爲一個ZooKeeper句柄添加多個權限的身份。

一、內置的鑑權模式

ZooKeeper提供了4種內置模式進行ACL的處理:數組

  • OPEN_ACL_UNSAFE常量緩存

    • 使用world做爲鑑權模式
    • 使用anyone做爲auth-info
  • 管理員所使用的super模式安全

    • 該模式不會列入到 ACL 中
    • 但能夠用於ZooKeeper的鑑權
    • 一個客戶端經過super鑑權模式鏈接到ZooKeeper後,不會被任何節點的ACL所限制
  • digest爲內置鑑權模式服務器

    • auth-info格式爲userid:passwd_digest(當調用addAuthInfo時須要設置ACL和userid:password信息。)
當ZooKeeper以一個空樹開始,只有一個znode節點:/,這個節點對全部人開放,咱們假設管理員Amy負責配置ZooKeeper服務,Amy建立/apps節點,用於全部使用服務的應用須要建立節點的父節點,她如今須要鎖定服務,因此她設置爲/和/apps節點設置的ACL爲:digest:amy:Iq0onHjzb4KyxPAp8YWOIC8zzwY=, READ | WRITE | CREATE | DELETE | ADMIN
其中passwd_digest爲用戶密碼的加密摘要。在這個ACL例子中,Iq0onHjzb4KyxPAp8YWOIC8zzwY=爲passwd_digest,所以當Amy調用addAuthInfo方法,auth參數傳入的爲amy:secret字符串的字節數組,Amy使用下面的DigestAuthenticationProvider來爲她的帳戶amy生成摘要信息。
java -cp $ZK_CLASSPATH \org.apache.zookeeper.server.auth.DigestAuthenticationProvider amy:secret.... amy:secret->amy:Iq0onHjzb4KyxPAp8YWOIC8zzwY=
amy:後面生成的字符串爲密碼摘要信息,也就是咱們在ACL記錄總使用的信息。當Amy須要向ZooKeeper提供鑑權信息時,她就要使用digest amy:secret。例如,當Amy使用zkCli.sh鏈接到ZooKeeper,她能夠經過如下方式提供鑑權信息:
[zk: localhost:2181(CONNECTED) 1] addauth digest amy:secret
爲了不在後面的例子中寫出全部的摘要信息,將使用XXXXX做爲佔位符來簡單的表示摘要信息。Amy想要設置一個子樹,用於一個名爲SuperApp的應用,該應用由開發人員Dom所開發,所以她建立了/apps/SuperApp節點,設置ACL以下:
digest:dom:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
digest:amy:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
該ACL由兩條記錄組成,一個由Dom使用,一個由Amy使用。這些記錄對全部以dom或amy密碼信息認證的客戶端提供了所有權限。注意,根據ACL中的Dom的記錄,他對/apps/SuperApp節點具備ADMIN權限,他有權限修改ACL,這就意味着Dom能夠刪除Amy訪問/apps/SuperApp節點的權限。固然Amy具備super的訪問權限,因此她能夠隨時訪問任何znode節點,即便Dom刪除了她的訪問權限。

Dom使用ZooKeeper來保存其應用的配置信息,所以他建立了/apps/SuperApp/config節點來保存配置信息。以後他使用咱們在以前例子中介紹的模式OPEN_ACL_UNSAFE來建立znode節點,由於Dom認爲/apps和/apps/SuperApp的訪問是受限制的,因此也能保護/apps/SuperApp/config節點的訪問。咱們後面就會看到,這樣作稱爲UNSAFE。網絡

咱們假設一個名爲Gabe的人具備ZooKeeper服務的網絡訪問權限。由於ACL的策略設置,Gabe沒法訪問/app或/apps/SuperApp節點,Gabe也沒法獲取/apps/SuperApp節點的子節點列表。可是,也許Gabe猜想Dom使用ZooKeeper保存配置信息,config這個名字對於配置文件信息也很是顯而易見,所以他鏈接到ZooKeeper服務,調用getData方法獲取/apps/SuperApp/config節點的信息。由於該znode節點採用了開放的ACL策略,Gabe能夠獲取該節點信息。還不止這些,Gabe能夠修改、刪除該節點,甚至限制/apps/SuperApp/config節點的訪問權限。多線程

假設Dom意識到這個問題,修改/apps/SuperApp/config節點的ACL策略爲:app

digest:dom:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
隨着事情的發展,Dom獲得一個新的開發人員Nico的幫助,來一同完善SuperApp。Nico須要訪問SuperApp的子樹,所以Dom修改了子樹的ACL策略,將Nico添加進來。新的ACL策略爲:
digest:dom:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
digest:nico:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN

注意:用戶名和密碼的摘要信息從何而來?

你也許注意到咱們用於摘要的用戶名和密碼彷佛憑空而來。實際上確實如此。這些用戶名或密碼不用對應任何真實系統的標識,甚至用戶名也能夠重複。也許有另外一個開發人員叫Amy,而且開始和Dom和Nico一同工做,Dom可使用amy:XXXXX來添加她的ACL策略,只是在這兩個Amy的密碼同樣時會發生衝突,由於這樣就致使她們倆能夠互相訪問對方的信息。

如今Dom和Nico具備了他們須要完成的SuperApp的所需的訪問權限。應用部署到生產環境,然而Dom和Nico並不想提供進程訪問ZooKeeper數據時所使用的密碼信息,所以他們決定經過SuperApp所運行的服務器的網絡地址來限制數據的訪問權限。例如全部10.11.12.0/24網絡中服務器,所以他們修改了SuperApp子樹的znode節點的ACL爲:

digest:dom:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
digest:nico:XXXXX, READ | WRITE | CREATE | DELETE | ADMIN
ip:10.11.12.0/24, READ
  • ip鑑權模式
ip鑑權模式須要提供網絡的地址和掩碼,由於須要經過客戶端的地址來進行ACL策略的檢查,客戶端在使用ip模式的ACL策略訪問znode節
點時,不須要調用addAuthInfo方法。
如今,任何在10.11.12.0/24網段中運行的ZooKeeper客戶端都具備SuperApp子樹的znode節點的讀取權限。該鑑權模式假設IP地址沒法被僞造,這個假設也許並不能適合於全部環境中。

二、SASL和Kerberos

SASL表示簡單認證與安全層(Simple Authentication and SecurityLayer)。SASL將底層系統的鑑權模型抽象爲一個框架,所以應用程序可使用SASL框架,並使用SASL支持多各類協議。在ZooKeeper中,SASL經常使用Kerberos協議,該鑑權協議提供以前咱們提到的那些缺失的功能。在使用SASL模式時,使用sasl做爲模式名,id則使用客戶端的 Kerberos的ID。
SASL是ZooKeeper的擴展鑑權模式,所以,須要經過配置參數或Java系統中參數激活該模式。若是你採用ZooKeeper的配置文件方式,須要使用authProvider.XXX配置參數,若是你想要經過系統參數方式,須要使用zookeeper.authProvider.XXX做爲參數名。這兩種狀況,XXX
能夠爲任意值,只要沒有任何重名的authProvider,通常XXX採用以0開始的一個數字。配置項的參數值爲org.apache.zookeeper.server.auth.SASLAuthenticationProvider,這樣就能夠激活SASL模式。

三、增長新鑑權模式

ZooKeeper中還可使用其餘的任何鑑權模式。對於激活新的鑑權模式來講只是簡單的編碼問題。在org.apache.zookeeper.server.auth包中提供了一個名爲AuthenticationProvider的接口類,若是你實現你本身的鑑權模式,你能夠將你的類發佈到服務器的classpath下,建立zookeeper.authProvider名稱前綴的Java系統參數,並將參數值設置爲你實現AuthenticationProvider接口的實際的類名。

2、恢復會話

  • 首先,應用程序的ZooKeeper狀態還處於客戶端崩潰時的狀態,其餘客戶端進程還在繼續運行,也許已經修改了ZooKeeper的狀態,所以,建議客戶端不要使用任何以前從ZooKeeper獲取的緩存狀態,而是使用ZooKeeper做爲協做狀態的可信來源。
  • 第二個重要問題是客戶端崩潰時,已經提交給ZooKeeper的待處理操做也許已經完成了,因爲客戶端崩潰致使沒法收到確認消息,ZooKeeper沒法保證這些操做確定會成功執行,所以,客戶端在恢復時也許須要進行一些ZooKeeper狀態的清理操做,以便完成某些未完成的任務。

3、當znode節點從新建立時,重置版本號

znode節點被刪除並重建後,其版本號將會被重置。若是應用程序在一個znode節點重建後,進行版本號檢查會致使錯誤的發生。

4、sync方法

由於與ZooKeeper的帶外通訊可能會致使某些問題,這種通訊經常稱爲隱蔽通道(hidden channel)
sync方法能夠用於處理這種狀況。sync爲異步調用的方法,客戶端在讀操做前調用該方法,假如客戶端從某些直接通道收到了某個節點變化的通知,並要讀取這個znode節點,客戶端就能夠經過sync方法,而後再調用getData方法:
...
zk.sync(path, voidCb, ctx); ①
zk.getData(path, watcher, dataCb, ctx); ②
...
/** ①sync方法接受一個path參數,一個void返回類型的回調方法的示例,一個上下文對象實例。
    ②getData方法與以前介紹的調用方式同樣。 */
sync方法的path參數指示須要進行操做的路徑。在系統內部,sync方法實際上並不會影響ZooKeeper,當服務端處理sync調用時,服務端會刷新羣首與調用sync操做的客戶端c所鏈接的服務端之間的通道,刷新的意思就是說在調用getData的返回數據的時候,服務端確保返回全部客戶端c調用sync方法時全部可能的變化狀況。在上面的隱蔽通道的狀況中,變化狀況的通訊會先於sync操做的調用而發生,所以當c收到getData調用的響應,響應中必然會包含c'所通知的變化狀況。注意,在此時該節點也可能發生了其餘變化,所以在調用getData時,ZooKeeper只保證全部變化狀況可以返回。
使用sync還有一個注意事項,這個須要深刻ZooKeeper內部的技術問題(你能夠選擇跳過此部分)。由於ZooKeeper的設計初衷是用於快速讀取以及以讀爲主要負載的擴展性考慮,因此簡化了sync的實現,同時與其餘常規的更新操做(如create、setData或delete)不一樣,sync操做並不會進入執行管道之中。sync操做只是簡單地傳遞到羣首,以後羣首會將響應包隊列化,傳遞給羣組成員,以後發送響應包。不過還有另一種可能,仲裁機制肯定的羣首l',如今已經不被仲裁組成員所承認,仲裁組成員如今選舉了另個羣首l',在這種狀況下,羣首l可能沒法處理全部的更新操做的同步,而sync調用也就可能沒法履行其保障。
ZooKeeper的實現中,經過如下方式處理上面的問題,ZooKeeper中的仲裁組成員在放棄一個羣首時會通知該羣首,經過羣首與羣組成員之間的tickTime來控制超時時間,當它們之間的TCP鏈接丟失,羣組成員在收到socket的異常後就會肯定羣首是否已經消失。羣首與羣組成員之間的超時會快於TCP鏈接的停止,雖然的確存在這種極端狀況致使錯誤的可能,可是在咱們現有經驗中還不曾遇到過。

5、順序性保障

雖然ZooKeeper聲明對一個會話中全部客戶端操做提供順序性的保障,但仍是會存在ZooKeeper控制以外某些狀況,可能會改變客戶端操做的順序。

一、鏈接丟失時的順序性

對於鏈接丟失事件,ZooKeeper會取消等待中的請求:

  • 對於同步方法的調用客戶端庫會拋出異常
  • 對於異步請求調用,客戶端調用的回調函數會返回結果碼來標識鏈接丟失

二、同步 API 和多線程的順序性

若是在多線程環境中使用同步API,須要特別注意順序性問題,一個同步ZooKeeper調用會阻塞運行,直到收到響應信息,若是兩個或更多線程向ZooKeeper同時提交了同步操做,這些線程中將會被阻塞,直到收到響應信息,ZooKeeper會順序返回響應信息,但操做結果可能因線程調度等緣由致使後提交的操做而先被執行。

三、同步和異步混合使用的順序性

6、數據字段和子節點的限制

ZooKeeper默認狀況下對數據字段的傳輸限制爲1MB,該限制爲任何節點數據字段的最大可存儲字節數,同時也限制了任何父節點能夠擁有的子節點數。

7、嵌入式 Zookeeper 服務器

缺點:

  • Zookeeper 不透明
  • 應用可用性和 ZooKeeper 可用性耦合
  • ZooKeeper經常被用來提供高可用服務,但對於應用中嵌入ZooKeeper的方式卻下降了其最強的優點。
相關文章
相關標籤/搜索