【笔记】从 Paxos 到 Zookeeper:第七章 Zookeeper 技术内幕之客户端
文章目錄
- 系統(tǒng)模型
- 數(shù)據(jù)模型
- 節(jié)點(diǎn)特性
- 版本-保證分布式數(shù)據(jù)原子性操作
- Watcher-數(shù)據(jù)變更通知
- ACL-保障數(shù)據(jù)安全
- 序列化與協(xié)議
- 序列化
- 通信協(xié)議
- 客戶(hù)端
- 一次會(huì)話(huà)的創(chuàng)建過(guò)程
- 初始化階段
- 會(huì)話(huà)創(chuàng)建階段
- 響應(yīng)處理階段
- 服務(wù)器地址列表
- ClientCnxn:網(wǎng)絡(luò) I/O
- 會(huì)話(huà)
系統(tǒng)模型
這一節(jié)首先從數(shù)據(jù)模型、節(jié)點(diǎn)特性、版本、Watcher 和 ACL 五方面來(lái)講述 Zookeeper 的系統(tǒng)模型。
數(shù)據(jù)模型
ZooKeeper 的視圖結(jié)構(gòu)和 Unix 文件系統(tǒng)非常類(lèi)似,但沒(méi)有引入傳統(tǒng)文件系統(tǒng)中目錄和文件的概念,而是使用了特有的“數(shù)據(jù)節(jié)點(diǎn)”概念,稱(chēng)為 ZNode。ZNode 是 Zookeeper 中數(shù)據(jù)的最小單元,每個(gè) ZNode 上都可以保存數(shù)據(jù),同時(shí)還可以?huà)燧d子節(jié)點(diǎn),因此構(gòu)成了一個(gè)層次化的命名空間,可以稱(chēng)之為“樹(shù)”。
廣義上說(shuō),事務(wù)是應(yīng)用程序中一系列操作的集合。狹義上的數(shù)據(jù)庫(kù)中的事務(wù)一般包含了一系列對(duì)數(shù)據(jù)庫(kù)有序的讀寫(xiě)操作,數(shù)據(jù)庫(kù)事務(wù)通常具備 ACID 特性。在 ZooKeeper 中事務(wù)是指能夠改變 ZooKeeper 服務(wù)器狀態(tài)的操作,一般包括數(shù)據(jù)節(jié)點(diǎn)創(chuàng)建、刪除、更新和客戶(hù)端會(huì)話(huà)創(chuàng)建于失效等操作。對(duì)于每一個(gè)事務(wù)請(qǐng)求,ZooKeeper 會(huì)為其分配一個(gè)全局唯一的事務(wù) ID,稱(chēng)為 ZXID,通常是一個(gè)64位的數(shù)字。
節(jié)點(diǎn)特性
ZooKeeper 中每個(gè)節(jié)點(diǎn)都是有生命周期的,其生命周期的長(zhǎng)短取決于數(shù)據(jù)節(jié)點(diǎn)的節(jié)點(diǎn)類(lèi)型。在 ZooKeeper 中,節(jié)點(diǎn)類(lèi)型可以分為:持久節(jié)點(diǎn) PERSISTENT、臨時(shí)節(jié)點(diǎn) EPHEMERAL、順序節(jié)點(diǎn) SEQUENTIAL 三大類(lèi),經(jīng)過(guò)組合使用可以產(chǎn)生四種組合型節(jié)點(diǎn)類(lèi)型:
ZooKeeper 上每個(gè)數(shù)據(jù)節(jié)點(diǎn)除了存儲(chǔ)數(shù)據(jù)內(nèi)容之外,還存儲(chǔ)了數(shù)據(jù)節(jié)點(diǎn)本身的一些狀態(tài)信息,這些狀態(tài)信息對(duì)應(yīng)原生 API 里的 Stat 類(lèi):
public class Stat implements Record {private long czxid; // 節(jié)點(diǎn)被創(chuàng)建時(shí)的事務(wù) IDprivate long mzxid; // 節(jié)點(diǎn)最近一次更新的事務(wù) IDprivate long ctime; // 創(chuàng)建時(shí)間private long mtime; // 最近更新時(shí)間private int version; // 版本號(hào)private int cversion; // 子節(jié)點(diǎn)的版本號(hào)private int aversion; // 節(jié)點(diǎn)的 ACL 版本號(hào)private long ephemeralOwner; // 創(chuàng)建該臨時(shí)節(jié)點(diǎn)的會(huì)話(huà)的 sessionID,如果是持久節(jié)點(diǎn),其值為 0private int dataLength; // 數(shù)據(jù)內(nèi)容長(zhǎng)度private int numChildren;// 子節(jié)點(diǎn)個(gè)數(shù)private long pzxid; // 子節(jié)點(diǎn)列表最后一次被修改的事務(wù) ID,不包含子節(jié)點(diǎn)內(nèi)容的修改 }版本-保證分布式數(shù)據(jù)原子性操作
ZooKeeper 為數(shù)據(jù)節(jié)點(diǎn)引入了版本的概念,每個(gè)數(shù)據(jù)節(jié)點(diǎn)都具有三種類(lèi)型的版本信息,對(duì)數(shù)據(jù)節(jié)點(diǎn)的任何變更都會(huì)引起版本號(hào)的變化。version 從 0 開(kāi)始,表示“節(jié)點(diǎn)自創(chuàng)建后,其內(nèi)容被更新過(guò) 0 次”,即使更新時(shí)內(nèi)容本身沒(méi)有變化,version 值也會(huì)發(fā)生變化。
version 屬性最大的用處是用來(lái)實(shí)現(xiàn)樂(lè)觀(guān)鎖機(jī)制中的“寫(xiě)入校驗(yàn)”,在 ZooKeeper 原生 API setData 方法里就有一個(gè) version 參數(shù)。
Watcher-數(shù)據(jù)變更通知
一個(gè)典型的發(fā)布訂閱系統(tǒng)能夠讓多個(gè)同時(shí)監(jiān)聽(tīng)某一主題對(duì)象,當(dāng)這個(gè)主題對(duì)象自身發(fā)生變化時(shí),通知所有訂閱者。ZooKeeper 中引入了 Watcher 機(jī)制來(lái)實(shí)現(xiàn)這種分布式的通知功能。ZooKeeper 允許客戶(hù)端向服務(wù)端注冊(cè)一個(gè) Watcher 監(jiān)聽(tīng),當(dāng)指定事件觸發(fā) Watcher 時(shí),服務(wù)端會(huì)向客戶(hù)端發(fā)送一個(gè)事件通知。
ZooKeeper 的 Watcher 機(jī)制主要包括客戶(hù)端線(xiàn)程、客戶(hù)端 WatchManager 和 ZooKeeper 服務(wù)器三個(gè)部分。客戶(hù)端在向 ZooKeeper 服務(wù)器注冊(cè) Watcher 的同時(shí),會(huì)把 Watcher 存儲(chǔ)在客戶(hù)端的 WatchManager 中。當(dāng)服務(wù)端觸發(fā) Watcher 事件后,會(huì)向客戶(hù)端發(fā)送通知,客戶(hù)端線(xiàn)程從 WatchManager 中取出對(duì)應(yīng)的 Watcher 對(duì)象來(lái)執(zhí)行回調(diào)邏輯。
接口類(lèi) Watcher 用于表示一個(gè)標(biāo)準(zhǔn)的事件處理器,其定義了事件通知相關(guān)邏輯,包含 KeeperState 和 EventType 兩個(gè)枚舉類(lèi),分別代表了通知狀態(tài)和事件類(lèi)型。同時(shí),也定義了回調(diào)方法 process。
public interface Watcher {void process(WatchedEvent var1);public interface Event {public static enum EventType {None(-1),NodeCreated(1), // None 之外的事件類(lèi)型,只在 KeeperState 為 SyncConnected 時(shí)出現(xiàn)NodeDeleted(2),NodeDataChanged(3),NodeChildrenChanged(4);private final int intValue;private EventType(int intValue) {this.intValue = intValue;}}public static enum KeeperState {/** @deprecated */@DeprecatedUnknown(-1),Disconnected(0),/** @deprecated */@DeprecatedNoSyncConnected(1),SyncConnected(3),AuthFailed(4), ConnectedReadOnly(5),SaslAuthenticated(6),Expired(-112);private final int intValue;private KeeperState(int intValue) {this.intValue = intValue;}}} }AuthFailed 事件的觸發(fā)條件并不簡(jiǎn)單是因?yàn)闆](méi)有權(quán)限,一般是因?yàn)椴捎昧隋e(cuò)誤的權(quán)限 Scheme。
回調(diào)方法 process 只有一個(gè)類(lèi)型為 WatchedEvent 的參數(shù),該類(lèi)有三個(gè)屬性:狀態(tài)、事件類(lèi)型、節(jié)點(diǎn)路徑。
public class WatchedEvent {private final KeeperState keeperState;private final EventType eventType;private String path; }客戶(hù)端在發(fā)送 Watch 信息給服務(wù)端時(shí),并沒(méi)有把 Watcher 對(duì)象發(fā)送過(guò)去,只是發(fā)送了 watcher 標(biāo)志位置為了 true。服務(wù)端收到請(qǐng)求后,判斷如果 watcher 為 true,就把代表客戶(hù)端服務(wù)端連接的對(duì)象 ServerCnxn 和節(jié)點(diǎn)路徑 Path 的映射關(guān)系放入 WatcherManager 中,也就是只是把需要 watcher 的連接存了起來(lái)。
ACL-保障數(shù)據(jù)安全
在 Unix 文件系統(tǒng)中,使用的是 UGO 權(quán)限控制機(jī)制,UGO 就是針對(duì)一個(gè)文件或目錄,對(duì)創(chuàng)建者(User)、創(chuàng)建者所在的組(Group)、其他用戶(hù)(Other)分別配置不同的權(quán)限。
在 ZooKeeper 中使用的權(quán)限控制方式叫 ACL,即訪(fǎng)問(wèn)控制列表,是一種新穎的、更細(xì)粒度的權(quán)限控制方式。ACL 機(jī)制有三個(gè)概念:權(quán)限模式(Scheme)、授權(quán)對(duì)象(ID)、權(quán)限(Permission),通常使用“scheme?permission”來(lái)表示一個(gè)有效的 ACL 信息。
權(quán)限模式用來(lái)確定權(quán)限驗(yàn)證中使用的檢驗(yàn)策略,有以下四種:
授權(quán)對(duì)象 ID 指的是權(quán)限賦予的用戶(hù)或一個(gè)指定實(shí)體,例如 IP 地址或機(jī)器。在不同的權(quán)限模式下,授權(quán)對(duì)象是不同的,IP 權(quán)限模式下授權(quán)對(duì)象是 IP,其他權(quán)限模式下授權(quán)對(duì)象都是權(quán)限標(biāo)識(shí)“username:password”。
權(quán)限是指通過(guò)權(quán)限檢查后運(yùn)行執(zhí)行的操作,在 ZooKeeper 中有五類(lèi):
以下命令的含義是,權(quán)限模式為 Digest,把數(shù)據(jù)節(jié)點(diǎn) /test 的全部權(quán)限 cdrwa 授予授權(quán)對(duì)象“wkp:NrLAZ6FuRnaPGI93r1uPKD67MLw=”。
setAcl /test digest:wkp:NrLAZ6FuRnaPGI93r1uPKD67MLw=:cdrwa盡管 ZooKeeper 已經(jīng)為我們提供了上述的四種權(quán)限模式,同時(shí)也提供給我們能夠自定義自己權(quán)限的方式——實(shí)現(xiàn)接口
org.apache.zookeeper.server.auth.AuthenticationProvider,并在啟動(dòng)參數(shù)或配置文件里配置接口實(shí)現(xiàn)即可。
序列化與協(xié)議
序列化
Jute 是 ZooKeeper 中的序列化組件,幾乎沒(méi)有其他組件使用,下面簡(jiǎn)單講講其序列化方法和原理。
每一個(gè)要使用 Jute 序列化的類(lèi),都需要實(shí)現(xiàn) Record 接口,其有兩個(gè)方法:serialize 和 deserialize。下面是一個(gè)實(shí)現(xiàn)了 Record 接口的 Bean 示例:
public interface Record {public void serialize(OutputArchive archive, String tag)throws IOException;public void deserialize(InputArchive archive, String tag)throws IOException; }public class TestBean implements Record {private int intV;private String stringV;public TestBean() {}public TestBean(int intV, String stringV) {this.intV = intV;this.stringV = stringV;}@Overridepublic void deserialize(InputArchive archive, String tag)throws IOException {archive.startRecord(tag);this.intV = archive.readInt("intV");this.stringV = archive.readString("stringV");archive.endRecord(tag);}@Overridepublic void serialize(OutputArchive archive, String tag) throws IOException {archive.startRecord(this, tag);archive.writeInt(intV, "intV");archive.writeString(stringV, "stringV");archive.endRecord(this, tag);} }對(duì) TestBean 進(jìn)行序列化和反序列化的方法是:
public class BinaryTest1 {public static void main(String[] args) throws IOException {ByteArrayOutputStream baos = new ByteArrayOutputStream();BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos);new TestBean(1, "testbean1").serialize(boa, "tag1");byte array[] = baos.toByteArray();ByteArrayInputStream bais = new ByteArrayInputStream(array);BinaryInputArchive bia = BinaryInputArchive.getArchive(bais);TestBean newBean1 = new TestBean();newBean1.deserialize(bia, "tag1");System.out.println("intV = " + newBean1.getIntV() + ",stringV = "+ newBean1.getStringV());bais.close();baos.close();} }我們可以看到 TestBean 實(shí)現(xiàn)了 Record 的兩個(gè)方法,其序列化主要是使用了 InputArchive 和 OutputArchive,它兩有多種實(shí)現(xiàn),最常用的是 BinaryOutputArchive 和 BinaryInputArchive。在上面的代碼里,序列化時(shí),便使用了 BinaryOutputArchive,看下面的源碼可知,其底層其實(shí)是把數(shù)據(jù)輸出到了 ByteArrayOutputStream。
public class BinaryOutputArchive implements OutputArchive {private ByteBuffer bb = ByteBuffer.allocate(1024);private DataOutput out;public static BinaryOutputArchive getArchive(OutputStream strm) {return new BinaryOutputArchive(new DataOutputStream(strm));}/** Creates a new instance of BinaryOutputArchive */public BinaryOutputArchive(DataOutput out) {this.out = out;}public void writeByte(byte b, String tag) throws IOException {out.writeByte(b);}public void writeBool(boolean b, String tag) throws IOException {out.writeBoolean(b);}public void writeInt(int i, String tag) throws IOException {out.writeInt(i);}public void writeLong(long l, String tag) throws IOException {out.writeLong(l);}public void writeFloat(float f, String tag) throws IOException {out.writeFloat(f);}public void writeDouble(double d, String tag) throws IOException {out.writeDouble(d);}/*** create our own char encoder to utf8. This is faster * then string.getbytes(UTF8).* @param s the string to encode into utf8* @return utf8 byte sequence.*/final private ByteBuffer stringToByteBuffer(CharSequence s) {bb.clear();final int len = s.length();for (int i = 0; i < len; i++) {if (bb.remaining() < 3) {ByteBuffer n = ByteBuffer.allocate(bb.capacity() << 1);bb.flip();n.put(bb);bb = n;}char c = s.charAt(i);if (c < 0x80) {bb.put((byte) c);} else if (c < 0x800) {bb.put((byte) (0xc0 | (c >> 6)));bb.put((byte) (0x80 | (c & 0x3f)));} else {bb.put((byte) (0xe0 | (c >> 12)));bb.put((byte) (0x80 | ((c >> 6) & 0x3f)));bb.put((byte) (0x80 | (c & 0x3f)));}}bb.flip();return bb;}public void writeString(String s, String tag) throws IOException {if (s == null) {writeInt(-1, "len");return;}ByteBuffer bb = stringToByteBuffer(s);writeInt(bb.remaining(), "len");out.write(bb.array(), bb.position(), bb.limit());}public void writeBuffer(byte barr[], String tag)throws IOException {if (barr == null) {out.writeInt(-1);return;}out.writeInt(barr.length);out.write(barr);}public void writeRecord(Record r, String tag) throws IOException {r.serialize(this, tag);}public void startRecord(Record r, String tag) throws IOException {}public void endRecord(Record r, String tag) throws IOException {} }BinaryInputArchive 也是類(lèi)似的,就不細(xì)講了。
通信協(xié)議
基于 TCP/IP 協(xié)議,Zookeeper 實(shí)現(xiàn)了自己的通信協(xié)議來(lái)玩按成客戶(hù)端與服務(wù)端、服務(wù)端與服務(wù)端之間的網(wǎng)絡(luò)通信,對(duì)于請(qǐng)求,主要包含請(qǐng)求頭和請(qǐng)求體,對(duì)于響應(yīng),主要包含響應(yīng)頭和響應(yīng)體。
對(duì)于請(qǐng)求協(xié)議而言,如下為獲取節(jié)點(diǎn)數(shù)據(jù)請(qǐng)求的完整協(xié)議定義:
圖里的 bit offset,其實(shí)應(yīng)該是字節(jié) byte offset,因?yàn)?4 個(gè) bit 位肯定不能完全表達(dá)數(shù)據(jù)長(zhǎng)度。其中 xid 用于記錄客戶(hù)端請(qǐng)求發(fā)起的先后序號(hào),用來(lái)確保單個(gè)客戶(hù)端請(qǐng)求的響應(yīng)順序,type 代表請(qǐng)求的操作類(lèi)型,如創(chuàng)建節(jié)點(diǎn)(OpCode.create)、刪除節(jié)點(diǎn)(OpCode.delete)、獲取節(jié)點(diǎn)數(shù)據(jù)(OpCode.getData)。
協(xié)議的請(qǐng)求主體內(nèi)容部分,包含了請(qǐng)求的所有操作內(nèi)容,不同的請(qǐng)求類(lèi)型請(qǐng)求體不同。對(duì)于會(huì)話(huà)創(chuàng)建而言,其請(qǐng)求體如下:
class ConnectRequest {int protocolVersion;long lastZxidSeen;int timeOut;long sessionId;buffer passwd;}Zookeeper 客戶(hù)端和服務(wù)器在創(chuàng)建會(huì)話(huà)時(shí),會(huì)發(fā)送 ConnectRequest 請(qǐng)求,該請(qǐng)求包含協(xié)議版本號(hào) protocolVersion、最近一次接收到服務(wù)器 ZXID lastZxidSeen、會(huì)話(huà)超時(shí)時(shí)間 timeOut、會(huì)話(huà)標(biāo)識(shí) sessionId 和會(huì)話(huà)密碼 passwd。
對(duì)于響應(yīng)協(xié)議而言,如下為獲取節(jié)點(diǎn)數(shù)據(jù)響應(yīng)的完整協(xié)議定義:
xid 與請(qǐng)求頭中的 xid 一致,zxid 表示 Zookeeper 服務(wù)器上當(dāng)前最新的事務(wù) ID,err 則是一個(gè)錯(cuò)誤碼,表示當(dāng)請(qǐng)求處理過(guò)程出現(xiàn)異常情況時(shí),就會(huì)在錯(cuò)誤碼中標(biāo)識(shí)出來(lái),常見(jiàn)的包括處理成功(Code.OK)、節(jié)點(diǎn)不存在(Code.NONODE)、沒(méi)有權(quán)限(Code.NOAUTH)。
協(xié)議的響應(yīng)主體內(nèi)容部分,包含了響應(yīng)的所有數(shù)據(jù),不同的響應(yīng)類(lèi)型請(qǐng)求體不同。對(duì)于會(huì)話(huà)創(chuàng)建而言,其響應(yīng)體如下:
class ConnectResponse {int protocolVersion;int timeOut;long sessionId;buffer passwd;}針對(duì)客戶(hù)端的會(huì)話(huà)創(chuàng)建請(qǐng)求,服務(wù)端會(huì)返回客戶(hù)端一個(gè) ConnectResponse 響應(yīng),該響應(yīng)體包含了版本號(hào) protocolVersion、會(huì)話(huà)的超時(shí)時(shí)間 timeOut、會(huì)話(huà)標(biāo)識(shí) sessionId 和會(huì)話(huà)密碼 passwd。
客戶(hù)端
客戶(hù)端是開(kāi)發(fā)人員使用 ZooKeeper 最主要的途徑,主要由以下幾個(gè)核心組件構(gòu)成:
一次會(huì)話(huà)的創(chuàng)建過(guò)程
初始化階段
會(huì)話(huà)創(chuàng)建階段
響應(yīng)處理階段
服務(wù)器地址列表
在使用ZooKeeper構(gòu)造方法時(shí),用戶(hù)傳入的ZooKeeper服務(wù)器地址列表,即connectString參數(shù),通常是這樣一個(gè)使用英文狀態(tài)逗號(hào)分隔的多個(gè)IP地址和端口的字符串:
192.168.0.1:2181,192.168.0.1:2181,192.168.0.1:2181在 3.2.0 之后的新版本客戶(hù)端里,也增加了命名空間特性。如果一個(gè) ZooKeeper 客戶(hù)端配置了 Chroot,那么客戶(hù)端對(duì)服務(wù)器的任何操作,都將會(huì)被限制在自己的命名空間下。
192.168.0.1:2181,192.168.0.1:2181,192.168.0.1:2181/namespaceZooKeeper客戶(hù)端允許我們將服務(wù)器的所有地址都配置在一個(gè)字符串上,于是一個(gè)問(wèn)題就來(lái)了:ZooKeeper客戶(hù)端在連接服務(wù)器的過(guò)程中,是如何從這個(gè)服務(wù)器列表中選擇服務(wù)器機(jī)器的呢?是按序訪(fǎng)問(wèn),還是隨機(jī)訪(fǎng)問(wèn)呢?
在 ZooKeeper 中,是通過(guò) HostProvider 來(lái)提供地址管理的,有個(gè) next 方法用于獲取下一個(gè)要訪(fǎng)問(wèn)的地址。其默認(rèn)實(shí)現(xiàn)是 StaticHostProvider,next 方法的邏輯是先將地址隨機(jī)打亂,組成一個(gè)循環(huán)隊(duì)列,然后一直按序訪(fǎng)問(wèn)。
public interface HostProvider {public int size();public InetSocketAddress next(long spinDelay);public void onConnected(); }默認(rèn)實(shí)現(xiàn)的 StaticHostProvider 邏輯比較簡(jiǎn)單,可以考慮對(duì)其進(jìn)行擴(kuò)展:
ClientCnxn:網(wǎng)絡(luò) I/O
ClientCnxn 是 ZooKeeper 客戶(hù)端的核心工作類(lèi),負(fù)責(zé)維護(hù)客戶(hù)端和服務(wù)端之間的網(wǎng)絡(luò)連接,并進(jìn)行一系列通信。ClientCnxn 有下面這些屬性:
可以看到隊(duì)列里元素類(lèi)型是 Packet,Packet 是對(duì)協(xié)議層的一個(gè)封裝,看起來(lái)屬性很多,但實(shí)際上只有 requestHeader、request、readOnly 會(huì)傳遞到服務(wù)端。
static class Packet {RequestHeader requestHeader; // 請(qǐng)求頭ReplyHeader replyHeader; // 響應(yīng)頭Record request; // 請(qǐng)求體Record response; // 響應(yīng)體ByteBuffer bb;/** Client's view of the path (may differ due to chroot) **/String clientPath; // 節(jié)點(diǎn)路徑/** Servers's view of the path (may differ due to chroot) **/String serverPath;boolean finished;AsyncCallback cb;Object ctx;WatchRegistration watchRegistration; // 注冊(cè)的 watcherpublic boolean readOnly;}會(huì)話(huà)
會(huì)話(huà)是 ZooKeeper 中最重要的概念之一,客戶(hù)端與服務(wù)端之間的任何交互操作都與會(huì)話(huà)息息相關(guān)。
會(huì)話(huà)狀態(tài)包括 CONNECTING、CONNECTED、RECONNECTING、RECONNECTED、CLOSED 等。
Session是Zookeeper中的會(huì)話(huà)實(shí)體,代表了一個(gè)客戶(hù)端會(huì)話(huà),其包含了如下四個(gè)屬性:
Zookeeper 為了保證請(qǐng)求會(huì)話(huà)的全局唯一性,在 SessionTracker 初始化時(shí),調(diào)用 initializeNextSession 方法生成一個(gè) sessionID,之后在 Zookeeper 運(yùn)行過(guò)程中,會(huì)在該 sessionID 的基礎(chǔ)上為每個(gè)會(huì)話(huà)進(jìn)行分配,初始化算法如下:
public static long initializeNextSession(long id) {long nextSid = 0;// 無(wú)符號(hào)右移8位使為了避免左移24后,再右移8位出現(xiàn)負(fù)數(shù)而無(wú)法通過(guò)高8位確定sid值nextSid = (System.currentTimeMillis() << 24) >>> 8;nextSid = nextSid | (id << 56);return nextSid; }其中的 id 表示配置在 myid 文件中的值,通常是一個(gè)整數(shù),如1、2、3。該算法的高 8 位確定了所在機(jī)器,后 56 位使用當(dāng)前時(shí)間的毫秒表示進(jìn)行隨機(jī)。SessionTracker 是 Zookeeper 服務(wù)端的會(huì)話(huà)管理器,負(fù)責(zé)會(huì)話(huà)的創(chuàng)建、管理和清理等工作。
Zookeeper的會(huì)話(huà)管理主要是通過(guò)SessionTracker來(lái)負(fù)責(zé),其采用了分桶策略(將類(lèi)似的會(huì)話(huà)放在同一區(qū)塊中進(jìn)行管理)進(jìn)行管理,以便Zookeeper對(duì)會(huì)話(huà)進(jìn)行不同區(qū)塊的隔離處理以及同一區(qū)塊的統(tǒng)一處理。
Zookeeper將所有的會(huì)話(huà)都分配在不同的區(qū)塊一種,分配的原則是每個(gè)會(huì)話(huà)的下次超時(shí)時(shí)間點(diǎn)(ExpirationTime)。ExpirationTime指該會(huì)話(huà)最近一次可能超時(shí)的時(shí)間點(diǎn)。同時(shí),Zookeeper Leader服務(wù)器在運(yùn)行過(guò)程中會(huì)定時(shí)地進(jìn)行會(huì)話(huà)超時(shí)檢查,時(shí)間間隔是ExpirationInterval,默認(rèn)為tickTime的值,ExpirationTime的計(jì)算時(shí)間如下
ExpirationTime = ((CurrentTime + SessionTimeOut) / ExpirationInterval + 1) * ExpirationInterval會(huì)了保持客戶(hù)端會(huì)話(huà)的有效性,客戶(hù)端會(huì)在會(huì)話(huà)超時(shí)時(shí)間過(guò)期范圍內(nèi)向服務(wù)端發(fā)送PING請(qǐng)求來(lái)保持會(huì)話(huà)的有效性(心跳檢測(cè))。同時(shí),服務(wù)端需要不斷地接收來(lái)自客戶(hù)端的心跳檢測(cè),并且需要重新激活對(duì)應(yīng)的客戶(hù)端會(huì)話(huà),這個(gè)重新激活過(guò)程稱(chēng)為T(mén)ouchSession。
當(dāng)SessionTracker的會(huì)話(huà)超時(shí)線(xiàn)程檢查出已經(jīng)過(guò)期的會(huì)話(huà)后,就開(kāi)始進(jìn)行會(huì)話(huà)清理工作,大致可以分為如下七步。
- 節(jié)點(diǎn)刪除請(qǐng)求,刪除的目標(biāo)節(jié)點(diǎn)正好是上述臨時(shí)節(jié)點(diǎn)中的一個(gè)。
- 臨時(shí)節(jié)點(diǎn)創(chuàng)建請(qǐng)求,創(chuàng)建的目標(biāo)節(jié)點(diǎn)正好是上述臨時(shí)節(jié)點(diǎn)中的一個(gè)。
- 對(duì)于第一類(lèi)請(qǐng)求,需要將所有請(qǐng)求對(duì)應(yīng)的數(shù)據(jù)節(jié)點(diǎn)路徑從當(dāng)前臨時(shí)節(jié)點(diǎn)列表中移出,以避免重復(fù)刪除,對(duì)于第二類(lèi)請(qǐng)求,需要將所有這些請(qǐng)求對(duì)應(yīng)的數(shù)據(jù)節(jié)點(diǎn)路徑添加到當(dāng)前臨時(shí)節(jié)點(diǎn)列表中,以刪除這些即將被創(chuàng)建但是尚未保存到內(nèi)存數(shù)據(jù)庫(kù)中的臨時(shí)節(jié)點(diǎn)。
當(dāng)客戶(hù)端與服務(wù)端之間的網(wǎng)絡(luò)連接斷開(kāi)時(shí),Zookeeper 客戶(hù)端會(huì)自動(dòng)進(jìn)行反復(fù)的重連,直到最終成功連接上 Zookeeper 集群中的一臺(tái)機(jī)器。
總結(jié)
以上是生活随笔為你收集整理的【笔记】从 Paxos 到 Zookeeper:第七章 Zookeeper 技术内幕之客户端的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一文了解如何使用移动应用安全组件Soot
- 下一篇: tif构建金字塔失败arcgis_Arc