栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

ZK网络通信源码剖析

ZK网络通信源码剖析

ZK网络通信源码剖析
  • NIOServerCnxnFactory工作流程源码剖析
    • AcceptThread剖析
    • SelectorThread剖析
    • WorkerThread剖析
    • ConnectionExpirerThread剖析
  • ZK通信优劣总结

Zookeeper 作为一个服务器,自然要与客户端进行网络通信, ZooKeeper 中使用 ServerCnxnFactory 管理与客户端的连接,其有两个实现,一个是NIOServerCnxnFactory ,使用Java原生 NIO 实现;一个是NettyServerCnxnFactory ,使用netty实现;
使用 ServerCnxn 代表一个客户端与服务端的连接。从单机版启动中可以发现 Zookeeper 默认通信组件为 NIOServerCnxnFactory ,他们和ServerCnxnFactory 的关系如下图:

概览图:

NIOServerCnxnFactory工作流程源码剖析

NIOServerCnxnFactory 启动时会启动四类线程:

它们的关系图如下:

AcceptThread剖析

为了更容易理解AcceptThread,我把它的结构和方法调用关系画了一个详细的流程图,如下图:

在 NIOServerCnxnFactory 类中有一个 AccpetThread 线程,为什么说它是一个线程?我们看下它的继承关系: AcceptThread > AbstractSelectThread > ZooKeeperThread > Thread ,该线程接收来自客户端的连接,并将其分配给selector thread(启动一个线程)。

该线程执行流程: run() 执行selector.select(),并调用 doAccept() 接收客户端连接,因此我们可以着重关注 doAccept() 方法,该类源码如下:


doAccept() 方法用于处理客户端链接,当客户端链接 Zookeeper 的时候,首先会调用该方法,调用该方法执行过程如下:

doAccept() 方法源码如下:

上面代码中 addAcceptedConnection 方法如下:

SelectorThread剖析

同样为了更容易梳理 SelectorThread ,我也把它的结构和方法调用关系梳理成了流程图,如下图:

该线程的主要作用是从Socket读取数据,并封装成 workRequest ,并将 workRequest 交给workerPool 工作线程池处理,同时将acceptedQueue中未处理的链接取出,并为每个链接绑定OP_READ 读事件,并封装对应的上下文对象 NIOServerCnxn。 SelectorThread 的run方法如下:

run() 方法中会调用 select(),而 select() 中的核心调用地方是 handleIO() ,我们看名字其实就知道这里是处理客户端请求的数据,但客户端请求数据并非在SelectorThread线程中处理,我们接着看 handleIO() 方法。

handleIO()方法会封装当前 SelectorThread 为 IOWorkRequest ,并将 IOWorkRequest 交给workerPool 来调度,而 workerPool 调度才是读数据的开始,源码如下:

WorkerThread剖析

WorkerThread相比上面的线程而言,调用关系颇为复杂,设计到了多个对象方法调用,主要用于处理IO,但并未对数据做出处理,数据处理将由业务链对象RequestProcessor处理,调用关系图如下:

ZooKeeper 中通过 WorkerService 管理一组 worker thread 线程,前面我们在看 SelectorThread 的时候,能够看到workerPool 的schedule方法被执行,如下图:

我们跟踪 workerPool.schedule(workRequest); 可以发现它调用了
WorkerService.schedule(workRequest) > WorkerService.schedule(WorkRequest, long) ,该方法创建了一个新的线程 ScheduledWorkRequest ,并启动了该线程,源码如下:

ScheduledWorkRequest 实现了 Runnable 接口,并在 run() 方法中调用了 IOWorkRequest中的doWork 方法,在该方法中会调用 doIO 执行IO数据处理,源码如下:

IOWorkRequest 的 doWork 源码如下:

接下来的调用链路比较复杂,我们把核心步骤列出,在能直接看到数据读取的地方详细分析源码。上面方法调用链路:NIOServerCnxn.doIO()>readPayload()>readRequest()>ZookeeperServer.processPacket(),最后一步方法是获取核心数据的地方,我们可以修改下代码读取数据:

ConnectionExpirerThread剖析

后台启动 ConnectionExpirerThread 清理线程清理过期的 session ,线程中无限循环,执行工作如下:

ZK通信优劣总结

Zookeeper在通信方面默认使用了NIO,并支持扩展Netty实现网络数据传输。相比传统IO,NIO在网络数据传输方面有很多明显优势,也就是BIO和NIO的区别。
ZK在使用NIO通信虽然大幅提升了数据传输能力,但也存在一些代码诟病问题:
1:Zookeeper通信源码部分学习成本高,需要掌握NIO和多线程
2:多线程使用频率高,消耗资源多,但性能得到提升
3:Zookeeper数据处理调用链路复杂,多处存在内部类,代码结构不清晰,写法比较经典

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/422523.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号