ZooKeeper-客户端连接ServerCnxn之NIOServerCnxn

背景

ServerCnxn代表了一个客户端与一个server的连接,其有两种实现,分别是NIOServerCnxnNettyServerCnxn,类图如下:
这里写图片描述
本文介绍ZooKeeper是如何通过NIOServerCnxn实现网络IO的.

处理read事件

发生时机

SocketChannel上有数据可读时,worker thread调用NIOServerCnxn.doIO()进行读操作

粘包拆包问题

处理读事件比较麻烦的问题就是通过TCP发送的报文会出现粘包拆包问题,Zookeeper为了解决此问题,在设计通信协议时将报文分为3个部分:

  1. 请求头和请求体的长度(4个字节)
  2. 请求头
  3. 请求体

注:(1)请求头和请求体也细分为更小的部分,但在此不做深入研究,只需知道请求的前4个字节是请求头和请求体的长度即可.(2)将请求头和请求体称之为payload
在报文头增加了4个字节的长度字段,表示整个报文除长度字段之外的长度.服务端可根据该长度将粘包拆包的报文分离或组合为完整的报文.NIOServerCnxn读取数据流程如下:

  1. NIOServerCnxn中有两个属性,一个是lenBuffer,容量为4个字节,用于读取长度信息.一个是incomingBuffer,其初始化时即为lenBuffer,但是读取长度信息后,就为incomingBuffer分配对应的空间用于读取payload
  2. 根据请求报文的长度分配incomingBuffer的大小
  3. 将读到的字节存放在incomingBuffer中,直至读满(由于第2步中为incomingBuffer分配的长度刚好是报文的长度,此时incomingBuffer中刚好时一个报文)
  4. 处理报文

代码如下:

 void doIO(SelectionKey k) throws InterruptedException {
        try {
            ...
           /*
            处理读操作的流程
            1.最开始incomingBuffer就是lenBuffer,容量为4.第一次读取4个字节,即此次请求报文的长度
            2.根据请求报文的长度分配incomingBuffer的大小
            3.将读到的字节存放在incomingBuffer中,直至读满
             (由于第2步中为incomingBuffer分配的长度刚好是报文的长度,此时incomingBuffer中刚好时一个报文)
            4.处理报文
            */
            if (k.isReadable()) {
                //若是客户端请求,此时触发读事件
                //初始化时incomingBuffer即时lengthBuffer,只分配了4个字节,供用户读取一个int(此int值就是此次请求报文的总长度)
                int rc = sock.read(incomingBuffer);
                if (rc < 0) {
                    throw new EndOfStreamException(
                            "Unable to read additional data from client sessionid 0x"
                                    + Long.toHexString(sessionId)
                                    + ", likely client has closed socket");
                }
                /*
                只有incomingBuffer.remaining() == 0,才会进行下一步的处理,否则一直读取数据直到incomingBuffer读满,此时有两种可能:
                1.incomingBuffer就是lenBuffer,此时incomingBuffer的内容是此次请求报文的长度.
                根据lenBuffer为incomingBuffer分配空间后调用readPayload().
                在readPayload()中会立马进行一次数据读取,(1)若可以将incomingBuffer读满,则incomingBuffer中就是一个完整的请求,处理该请求;
                (2)若不能将incomingBuffer读满,说明出现了拆包问题,此时不能构造一个完整的请求,只能等待客户端继续发送数据,等到下次socketChannel可读时,继续将数据读取到incomingBuffer中
                2.incomingBuffer不是lenBuffer,说明上次读取时出现了拆包问题,incomingBuffer中只有一个请求的部分数据.
                而这次读取的数据加上上次读取的数据凑成了一个完整的请求,调用readPayload()
                 */

                if (incomingBuffer.remaining() == 0) {
                    boolean isPayload;
                    if (incomingBuffer == lenBuffer) {
                        // start of next request
                        //解析上文中读取的报文总长度,同时为"incomingBuffer"分配len的空间供读取全部报文
                        incomingBuffer.flip();
                        //为incomeingBuffer分配空间时还包括了判断是否是"4字命令"的逻辑
                        isPayload = readLength(k);
                        incomingBuffer.clear();
                    } else {
                        //2.incomingBuffer不是lenBuff
<think>好的,我现在需要帮助用户解决关于查看ZooKeeper日志中提交全局会话信息的问题。用户提到了“log committing global session”和“LearnerSessionTracker”,我需要结合这些关键词和提供的引用资料来分析。 首先,根据引用[3],ZooKeeper使用Session来管理客户端连接,当连接断开或超时,Session会被删除。同时,引用[2]提到本地Session可以升级为全局Session,这可能与用户提到的“committing global session”有关。全局Session可能涉及跨服务器的协调,因此日志中会有相关记录。 接下来,用户想知道如何查看这些日志。根据引用[1],ZooKeeper的日志分为snap数据和事务日志,分别存储在dataDir和dataLogDir。事务日志可能包含Session操作的记录,但用户需要查看的是服务器运行日志,通常位于日志文件中,比如zookeeper.log。 然后,关于LearnerSessionTracker,引用[3]提到ZK使用桶管理机制来管理Session和连接,而LearnerSessionTracker可能属于这一机制的一部分。当Leader处理Session创建或升级时,Follower会通过事务日志同步这些操作,如引用[5]所述,Follower收到Proposal后会将事务日志落盘,这可能包括Session的提交信息。 因此,用户需要检查ZooKeeper服务器的事务日志和运行日志,查找与SessionTracker和全局Session相关的条目。可能需要调整日志级别为DEBUG或INFO,并注意时间戳和Session ID。同时,如果Session管理涉及ZooKeeper的节点操作,引用[4]提到配置信息存储在永久节点中,但Session通常是临时节点,但这里用户关注的是日志而非节点数据。 总结步骤:确定日志文件位置,检查运行日志中的SessionTracker类输出,分析事务日志,调整日志级别,注意时间戳和Session ID。同时结合引用的资料确认机制的正确性。</think>要查看ZooKeeper日志中关于提交全局会话的信息,需重点关注以下三个步骤: ### 一、确认日志文件位置 1. **事务日志路径**:根据引用[1],事务日志存储在`dataLogDir`配置项指定的目录(示例路径:`/data/1/zookeeper/logs`) 2. **服务器运行日志**:默认包含Session操作的日志文件通常命名为`zookeeper.log`,具体路径取决于ZooKeeper的日志配置 ### 二、关键日志特征 在日志中搜索以下特征内容: ``` LearnerSessionTracker - Committing global session ``` 典型日志条目示例: ``` INFO [LearnerSessionTracker:256] - Committing global session 0x12345678, timeout=30000 ``` 此日志条目表示SessionTracker正在提交一个全局会话,包含Session ID(如`0x12345678`)和超时时间[^3][^5] ### 三、关联机制分析 1. **Session升级过程**:当本地Session(localSession)满足条件时会升级为全局Session,此过程会触发日志记录(引用[2]中提到的`localSessionsUpgradingEnabled`配置项控制该行为) 2. **事务同步机制**:如引用[5]所述,Follower节点通过SyncRequestProcessor将事务日志落盘,全局Session的提交会通过ZAB协议广播到集群 ### 四、日志增强方法 若默认日志级别未显示相关信息,可临时调整日志级别: ```bash # 修改log4j.properties文件 zookeeper.log.threshold=DEBUG ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值