ZooKeeper技術內幕-系統模型
阿新 • • 發佈:2018-11-26
系統模型
- 資料模型
- ZooKeeper檢視機構和標準Unix 檔案系統非常相似,使用了資料節點(ZNode)
- ZNode是ZooKeeper資料的最小單元
- ZNode可以掛在子節點,儲存資料資訊
- ZNode層次化,樹結構儲存
- 在ZooKeeper中,事務操作指的是能改變ZooKeeper伺服器狀態的操作
- 包括資料節點的建立、刪除、資料內容修改、客戶端會話的建立和失效
- 每一個事務請求,ZooKeeper為其分配全域性唯一id:zxid
節點特性
- 在ZooKeeper中,資料節點可分為:持久節點(PERSISTENT)、臨時節點(EPHEMERAL)、順序節點(SEQUENTIAL)三大類,四種組合
- PERSISTENT
- PERSISTENT_SEQUENTIAL(順序節點被建立時,節點名一個數字字尾,數字字尾最大值是最大整數)
- EPHEMERAL(臨時節點不能建立子節點)
- EPHEMERAL_SEQUENTIAL
- 狀態資訊
版本--保證分散式資料原子性操作
- ZooKeeper為資料節點引入版本資訊的概念
- ZooKeeper版本的概念是指:對資料節點內容、子節點列表、節點ACL資訊的修改次數
- ZooKeeper的 version屬性正是用來實現樂觀鎖的 “寫入校驗”
- setDataRequest 方法中檢查版本號
- 如果version ==-1 代表忽略檢查
悲觀鎖(悲觀併發控制):
- 一種非常嚴格的併發控制策略
- 具有強烈的獨佔性和排他性,能夠有效地避免不同事務對同一資料併發更新而造成造成的資料一致性問題
- 悲觀鎖適合解決那些對於資料更新競爭非常激烈的場景
- 簡單粗暴的解決併發控制問題
- 從頭到尾加鎖,假定資料併發操作一定會相互干擾
樂觀鎖(樂觀併發控制):
- 常見併發控制策略
- 比較寬鬆友好
- 假定很多事務不會相互干擾,但是也存在相互影響的可能,
- 在提交前進行檢查,如果已經被修改,全部回滾
- 資料讀取,寫入校驗,資料寫入三個階段
- jdk CAS是最典型的樂觀鎖,全稱為Compare-And-Swap
Watcher----資料變更的通知
- Watcher 介面
- 表示一個標準的事件處理器,定義了事件通知相關的邏輯
- 包含 KeeperState(通知狀態)、EventType(事件型別) 倆列舉類
- 事件回撥方法 process(WatchedEvent event);
Watcher 事件
- 相同事件型別,在不同通知狀態代表含義不同
- NodeDataChanged 事件
- 無論資料內容、資料版本號變化,都會觸發
- 因為只要客戶呼叫資料更新介面,就會改變版本號,哪怕內容沒變
- NodeChildrenChanged 事件
- 子節點列表變化:子節點個數和組成情況的變更
- 內容變化不會觸發
- AuthFailed 事件
- 以下面倆程式為例:
- 第一個會丟擲NoAuthException
- 第二個丟擲AuthFailedException,同時受到對應的事件通知(AuthFailed,None)
- 以下面倆程式為例:
- 回撥函式 process()
- 當ZooKeeper向客戶端傳送Watcher事件通知時,客戶端就會對相應的process方法進行回撥
- 和
- WatchedEvent 和 WatcherEvent
- 二者描述的都是服務端事件
- WatchedEvent 是一個邏輯封裝,服務端和客戶端執行過程中所需的邏輯物件
- WatcherEvent 實現了序列化介面,進行傳輸的封裝
- 服務端生成WatchedEvent 之後,會對其進行封裝成WatcherEvent 傳輸到客戶端
- 客戶端拿到WatcherEvent傳遞給process方法,還原成WatchedEvent
- 事件封裝都及其簡單,比如:ZNode 資料內容變更事件
- 客戶端只能收到簡單資訊:
- 客戶端無法獲取到更新後的新資料,需要客戶端再次主動請求獲取,這是Watcher機制非常重要的特性
- 工作機制:
- 包括三個部分:客戶端註冊、服務端處理、客戶端回撥
客戶端註冊Watcher:
- getData、getChildren、exist三個方法可向ZooKeeper註冊Watcher
- getData為例:
- 註冊後,客戶端首先會對請求request 進行標記(“使用Watcher監聽”),同時會封裝一個Watcher的註冊資訊WatcherRegistration
- WatcherRegistration用於暫時儲存資料節點的路徑和Watcher的對應關係
- 註冊後,客戶端首先會對請求request 進行標記(“使用Watcher監聽”),同時會封裝一個Watcher的註冊資訊WatcherRegistration
- ZooKeeper中最小的通訊協議單元是 Packet
- 客戶端和服務端的任何網路傳輸都需要封裝成Packet物件
- 在客戶端中WatcherRegistration會被封裝到packet物件,
- 然後放入傳送佇列,等待客戶端傳送
- 然後,客戶端回想服務端傳送請求,同時等待回覆
- 完成請求傳送後,會有客戶端SendThread執行緒的 readResponse 負責接收服務端響應
- finishPacket會從Packet中取出Watcher 註冊到 ZKWatchManager
- register方法中會將暫時儲存在WatcherRegistration 的Watcher 物件轉交給 ZKWatchManager 的 dataWatchers
- dataWatchers是 Map 型別,用於將資料節點的路徑和Watcher物件一一對應
- dataWatchers是 Map 型別,用於將資料節點的路徑和Watcher物件一一對應
- 客戶端每呼叫一次getData,就會註冊一個Watcher,這些Watcher實體都會被傳輸到服務端嗎?
- 不是,都傳的話服務端記憶體肯定吃不消
- WatchRegistration 封裝如Packet 進行傳輸,並不是對物件進行完全序列化
- 如下原始碼,只將requestHeader、request兩個屬性進行序列化
- WatchRegistration 並沒有序列化到底層位元組陣列中,不會進行網路傳輸
服務端處理Watcher
- 客戶端不會將Watcher 真正傳遞給服務端,服務端是如何完成註冊呢?
- FinalRequestProcessor 裡的processRequest 判斷是否需要註冊
- getData中的 getWatch方法 判斷是否需要註冊
- 傳入 ServerCnxn 介面例項(代表客戶端和服務端連線介面)
- 預設是 NIOServerCnxn,也可引入Netty,NettyServerCnxn
- 資料節點路徑和ServerCnxn被存入WatchManager 的 watchTables、watch2Paths
- WatchManager 還負責事件觸發,移除已經觸發的事件
- DataTree 會託管兩個 WatchManager:dataWatches、childWatches
Watcher觸發
- NodeDataChanged事件觸發條件是:watcher 監聽的資料節點資料內容變更
- process 實際呼叫的是:
- 服務端 watcher本質儲存的是 ServerCnxn
- process方法邏輯非常簡單,不是客戶端的真正邏輯,僅僅是向客戶端傳送一個事件通知
- process 實際呼叫的是:
客戶端回撥watcher
- SendThread 接收事件通知
- XID 是 -1 代表是一個通知型別的響應
- 四個步驟:
- 反序列化,從response 中拿到WatcherEvent
- 處理 chrootPath,生成相關中間路徑
- 還原 WatchedEvent,將WatcherEvent 轉換成WatchedEvent
- 回撥 Watcher ,最後將WatchedEvent 交給 eventThread(在下一輪中進行Watcher 回撥)
- EventThread 處理事件通知:
- 專門用來處理服務端事件通知的執行緒
- queueEvent方法
- 從 ClientWatchManager 取出所有相關watchers
- 獲取到相關watcher,放入waitingEvents 佇列
- 待處理watcher 佇列
- EventThread 的run() 方法會不斷處理該佇列
ACL---保障資料的安全
- ACL(Access control list):訪問控制列表
- 細粒度
- 如何保障ZooKeeper 誤操作帶來的資料隨意變更
- UGO(user group others)許可權控制機制
- 粗粒度許可權控制機制
- 包含內容:
- 許可權模式(Scheme):
- ip模式:
- ip:168.192.10.3
- 表示許可權控制針對該ip
- Digest模式
- 類似:使用者名稱:密碼
- world 模式
- 最開放,所有使用者不需要許可權校驗都可使用
- super
- 超級使用者,可以對ZooKeeper任何節點進行任何操作
- 運維人員清理垃圾資料
- ip模式:
- 授權物件(id):
- 許可權(Permission):
- 被允許的操作
- 許可權模式(Scheme):
許可權擴充套件體系
註冊許可權控制器
- 兩種方式:
- 系統屬性
- 檔案配置