天天看點

吃透 Zookeeper 網絡通信協定實作以及源碼

一提到網絡通信協定,我們都知道最常用的網絡通信協定就是這個 TCP/IP 協定,而 Zookeeper 就是基于 TCP/IP 協定實作了自己的通信方式。

ZooKeeper 協定簡述

Zookeeper 的通信協定分為兩部分,請求協定和響應協定。在 ZooKeeper 中一次用戶端的請求協定由請求頭、請求體組成。而在一次服務端的響應協定中由響應頭和響應體組成。

介紹完了 ZooKeeper 中的網絡通信協定的結構後。接下來我們就詳細來看一下在 ZooKeeper 中的内部對于網絡通信協定的底層是怎麼樣實作的。

ZooKeeper 協定的底層實作

請求協定是 Zookeeper Client 向 Zookeeper Server 發送請求時所使用的協定,包含了請求頭和請求體。比如我們經常用到的會話建立、資料節點查詢等操作。都是 Zookeeper 用戶端通過網絡向 ZooKeeper 服務端發送請求協定完成的。

Zookeeper 的請求協定

現在,我們已經知道什麼是請求協定了,接着我們就來看一下請求協定中的請求頭的底層實作原理。

我們知道,在 Zookeeper 中使用了 RequestHeader 類作為請求頭。

RequestHeader 類實作了 Record 接口,用于之後在網絡傳輸中進行序列化操作。在 RequestHeader 類中隻有兩個屬性字段分别是 xid 和 type。xid 代表用戶端序号,主要用于記錄用戶端請求的發起順序。type 代表請求操作的類型。

// RequestHeader 類實作了 Record 接口來進行序列化操作
public class RequestHeader implements Record{
    // 用戶端序号,記錄用戶端請求發起的順序
    private int xid;
    // 請求類型
    private int type;
}           

到這裡,想必你已經對請求協定中的請求頭的底層實作的原理有了一定的掌握,接着來我們來看看請求協定的請求體。

協定的請求體包括了協定處理邏輯的全部内容,一次會話請求的所有操作内容都涵蓋在請求體中。在 ZooKeeper 的内部實作中,根據不同的請求操作類型,會采用不同的結構封裝請求體。接下來我們以會話建立、節點查詢、節點更新三種類型的請求分别介紹相對應的請求體,深入底層看看 ZooKeeper 在内部是如何實作的。

會建立話請求

當 ZooKeeper 用戶端發起會話時,會向服務端發送一個會話建立請求,該請求的作用就是通知 ZooKeeper 服務端需要處理一個來自用戶端的通路連結。

而服務端處理會話建立請求時所需要的所有資訊都包括在請求體内,使用 ConnectRequest 類來封裝請求體,ConnectRequest 類實作了 Record 接口來進行序列化操作,其内部一共包括了五種屬性字段。

// ConnectRequest 類實作了 Record 接口來進行序列化操作
public class ConnectRequest implements Record {
    // 請求協定的版本資訊
    private int protocolVersion;
    // 最後一次接收到響應的服務端 zxid 序号
    private long lastZxidSeen;
    // 會話的逾時時間
    private int timeOut;
    // 會話辨別符 sessionId 
    private long sessionId;
    // 會話的密碼 password
    private byte[] passwd;
}           

節點查詢請求

在我們通過用戶端 API 查詢 ZooKeeper 伺服器上的資料節點時,用戶端會向服務端發送 GetDataRequest 會話請求。

使用 GetDataRequest 類來封裝請求體,GetDataRequest 類實作了 Record 接口用于序列化操作。其具有兩個屬性分别是字元類型 path 以及布爾類型 watch。

// GetDataRequest 類實作了 Record 接口來進行序列化操作
public class GetDataRequest implements Record {
    // 節點全路徑
    private String path;
    // 是否對該節點開啟監聽
    private boolean watch;
}           

接着,我們來看一下最後一種會話操作類型即節點的更新操作。

當 Zookeeper 用戶端向 Zookeeper 服務端發送節點查詢的請求時,其在網絡上實際發送的是更新操作的請求協定。而在 ZooKeeper 中對于協定内部的請求體,ZooKeeper 使用 SetDataRequest 類進行請求體的封裝。在 SetDataRequest 内部也包含了三種屬性,分别是 path 、data 以及 version。

// GetDataRequest 類實作了 Record 接口來進行序列化操作
public class GetDataRequest implements Record {
    // 節點全路徑
    private String path;
    // 是否對該節點開啟監聽
    private boolean watch;
}           

到目前為止,我們已經介紹完了 ZooKeeper 用戶端在一次網絡會話請求中所發送的請求協定的内部結構和底層實作。介紹完 Zookeeper 的請求協定之後,接下來我們繼續學習 Zookeeper 的響應協定。

Zookeeper 的響應協定

簡單的說,響應協定會在接收到 Zookeeper 用戶端的請求後,對請求協定進行解析并作出響應。和 Zookeeper 的請求協定相對應的,Zookeeper 的響應協定也是由響應頭和響應體組成,響應體也需要根據不同的請求類型來封裝響應體。

Zookeeper 服務端在接收到 Zookeeper 用戶端發送請求之後,由 ReplyHeader 類來解析請求頭并對響應頭進行封裝。

ReplyHeader 類實作了 Record 接口進行序列化操作,其包括三屬性分别是 xid、zxid 以及 err。

// ReplyHeader 類實作了 Record 接口來進行序列化操作
public class ReplyHeader implements Record {
    // 用戶端序号,記錄用戶端請求發起的順序
    private int xid;
    // 事務id
    private long zxid;
    // 錯誤狀态碼
    private int err;
}           

請求協定與響應請求類似的,在 ZooKeeper 的内部實作中,根據不同的響應操作類型,會采用不同的結構封裝響應體。接下來我們以會話建立、節點查詢、節點更新三種類型的響應分别介紹相對應的響應體,深入底層看看 ZooKeeper 在内部是如何實作的。

會話建立響應

當 Zookeeper 用戶端發起的一次會話連接配接請求,ZooKeeper 服務端在處理後,Zookeeper 服務端會傳回給 Zookeeper 用戶端一個 Response 響應。

ZooKeeper 是通過 ConnectRespose 類來實作的,ConnectRespose 類實作了 Record 接口進行序列化操作,在該類中有四個屬性,分别是 protocolVersion、timeOut、sessionId以及 passwd。

// ConnectResponse 類實作了 Record 接口來進行序列化操作
public class ConnectResponse implements Record {
    // 請求協定的版本資訊
    private int protocolVersion;
    // 會話逾時時間
    private int timeOut;
    // 會話辨別符 
    private long sessionId;
    // 會話密碼
    private byte[] passwd;
}           

響應查詢節點

同樣的,在 Zookeeper 用戶端發起查詢節點資料的請求時,Zookeeper 服務端根據用戶端發送的節點路徑,并驗證用戶端具有相應的權限後,會将節點資料傳回給用戶端。

ZooKeeper 服務端通過 GetDataResponse 類來封裝查詢到的節點相關資訊到響應協定的請求體中。GetDataResponse 類實作了 Record 接口進行序列化操作,其中包括兩個屬性字段分别是 data 和 stat。

// GetDataResponse 類實作了 Record 接口來進行序列化操作
public class GetDataResponse implements Record {
    // 節點資料的内容
    private byte[] data;
    // 節點的狀态資訊
    private org.apache.zookeeper.data.Stat stat;
}