无知的 TonySeek

Yet Another Seeker

ZooKeeper Session ID 的组成

ZooKeeper 的 Session ID 是每个会话的唯一标识,记录为一个 64 位的长整型。 只要对应会话未被服务器端主动关闭,它就一定能唯一关联上。且会话初次建立时 Session ID 会被写入 transaction log,这意味着会话是跨节点共享的。 客户端和一个节点断开连接后,只要会话还没过期,就仍然可以使用相同的 Session ID 连到另一个节点。这个会话创建的所有临时 ZNodes 也会相应保留。

指标显示 ZooKeeper 经常出现大量 outstanding requests (即节点上排队待处理的请求) 时,我们就需要诊断大量请求来自哪个 ZooKeeper 节点。无论是 leader、follower 还是 observer 节点都在本地处理读请求而将写请求转发给 leader 发起投票,所以节点自身的 outstanding requests 指标可能因参与投票而不是处理本节点的请求而飙高, 从而不一定能准确体现出请求来源。这个时候从 transaction log 中找出大量请求的 Session ID 就成了一种有效的诊断方法。

ZooKeeper 的官方文档非常惜墨,介绍了 Session ID 的作用而没有介绍其组成。 所以只能从源码看:

./src/org/apache/zookeeper/server/SessionTrackerImpl.java
 // ...

 public static long initializeNextSession(long id) {
     long nextSid = 0;
     nextSid = (System.currentTimeMillis() << 24) >>> 8;
     nextSid =  nextSid | (id <<56);
     return nextSid;
 }

 // ...
     this.nextSessionId = initializeNextSession(sid);
 // ...

很显然了,Session ID 的 64 个位的布局是这样的:

字段 (高位到低位) sid 时间戳 空白
长度 8 40 16

其中时间戳部分为会话创建时间被截断的低 40 位,而 sid 就是 ZooKeeper 节点的 myid 数字。对比 zoo.cfg 中的成员列表,就能找出会话创建时所在的 ZooKeeper 节点了。

借助社区第三方实现的 Python 脚本 zk-txnlog-tools 可以很容易地在 ZooKeeper 的 transaction log 中找出历史操作和相应的 Session ID, 结合 shell 工具过滤之后再从 Session ID 抽出 myid 汇总即得到每个节点的操作连接数。

example.py
 import collections

 sessionid_list = open('sessionid_list.txt').read().strip().split('\n')
 counter = collections.Counter(
     int(sessionid, 16) >> 56 for sessionid in sessionid_list)
 print(counter)  # output: Counter({1: 1171, 2: 1442, 3: 541})

嗯… 很惭愧,只写了一点微小的姿势,谢谢大家。

ZooKeeper

Comments