zookeeper3.x源码走读—NIOServerCnxnFactory服务的启动

本文深入解析ZooKeeper中核心组件NIOServerCnxnFactory的启动流程,包括线程池创建、连接管理、事件处理及连接过期处理机制。了解其如何构建高效的服务框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

本文讲述zookeeper的NIOServerCnxnFactory类的对象启动过程的实现代码。
该类实现了zookeeper的主要的后台服务,用来接收并处理客户端的各种命令,处理各种任务。

NIOServerCnxnFactory.start()启动服务

该函数用来启动后台服务,主要完成以下事项:

  1. 创建一个WorkerService,该线程执行器的创建和管理。
  2. 启动所有的SelectorThread线程,处理已经和客户端建立好的连接发送过来的连接请求。
  3. 启动AcceptThread线程,该线程用来接收客户端的连接请求,完成连接,并把完成的连接交给SelectorThread线程接手
  4. 启动expirerThread线程,该线程用来处理断开,或产生异常的连接

代码实现如下:

public void start() {
    stopped = false;
    // 启动worker线程池
    if (workerPool == null) {
        workerPool = new WorkerService(
            "NIOWorker", numWorkerThreads, false);
    }
    // 启动selecotr线程池
    for(SelectorThread thread : selectorThreads) {
        if (thread.getState() == Thread.State.NEW) {
            thread.start();
        }
    }
    // 启动accept线程
    // ensure thread is started once and only once
    if (acceptThread.getState() == Thread.State.NEW) {
        acceptThread.start();
    }
    // 启动终止线程
    if (expirerThread.getState() == Thread.State.NEW) {
        expirerThread.start();
    }
}

AcceptThread运行流程

实现该线程的主类是:AcceptThread

功能:该线程主要是接收来自客户端的连接请求,并完成三次握手,建立tcp连接。主要的实现流程如下:

  1. 在run()函数中实现线程的主要逻辑。在run()函数中主要调用select()函数。
  2. 在select()函数中,会调用java的nio库中的函数:selector.select()对多个socket进行监控,看是否有读、写事件发生。
  3. 若没有读、写事件发生,该函数会一直阻塞。
  4. 若有能够accepted事件发生,则调用doAccept()函数进行处理。
  5. 在函数doAccept中,会调用socket的accept函数,来完成和客户端的三次握手,建立起tcp连接。然后把已经完成连接的socket,设置成非阻塞:sc.configureBlocking(false);
  6. 接下来选择一个selector线程,并把连接好的socket添加到该selector线程的acceptedQueue队列中,该队列的声明是:private final Queue acceptedQueue,定义是:acceptedQueue = new LinkedBlockingQueue();添加的函数是:addAcceptedConnection。
  7. 可见,accepted队列是一个阻塞队列,添加到该队列后,就需要selector线程来接管已连接socket的后续的消息,所以需要唤醒selector队列。
  8. 在addAcceptedConnection把已连接socket添加到阻塞队列中后,调用wakeupSelector();唤醒对应的selector线程。该函数很简单,就是直接调用Selector的wackup()函数。

SelectorThread

该线程接管连接完成的socket,接收来自该socket的命令处理命令,把处理结果返回给客户端。

构造函数

创建了两个阻塞队列,一个用于接收,一个用于监控还没有完成连接的socket。

public SelectorThread(int id) throws IOException {
    super("NIOServerCxnFactory.SelectorThread-" + id);
    this.id = id;
    // socket完成接收的队列
    acceptedQueue = new LinkedBlockingQueue<SocketChannel>();
    // 选择队列
    updateQueue = new LinkedBlockingQueue<SelectionKey>();
}

主处理流程

主处理流程在run函数中,实现代码如下:

public void run() {
    try {
        while (!stopped) {
            try {
                select();
                processAcceptedConnections();
                processInterestOpsUpdateRequests();
            } catch (RuntimeException e) {
                LOG.warn("Ignoring unexpected runtime exception", e);
            } catch (Exception e) {
                LOG.warn("Ignoring unexpected exception", e);
            }
        }
        ...
    }
}
处理客户端请求总体流程
  1. 在主流程中,会调用select()函数来监控socket是否有读和写事件,若有读和写事件会调用handleIO(key)函数对事件进行处理。
  2. 在handleIO中,会启动woker线程池中的一个worker来处理这个事件,处理事件的主类是ScheduledWorkRequest,最终会调用run函数中的workRequest.doWork();来处理请求。
  3. 在IOWorkRequest.doWork()中会判断key的合法性,然后调用NIOServerCnxn.doIO(key)来处理事件,
  4. 在doIO函数中,对读的事件会调用readPayload()函数来处理,对于写事件会调用handleWrite(k)来处理。
  5. 当消息接收完成后会调用readRequest()函数来处理消息,之后会调用ZooKeeperServer.processPacket函数来分解消息包并处理请求。
  6. 在processPacket函数中会根据认证方式的不同来处理,最后会调用submitRequest函数来处理不同类型的请求。
  7. 最后进入请求的处理链:PrepRequestProcessor->SyncRequestProcessor->FinalRequestProcessor,在这个处理链中共有三个阶段,消息被不同的阶段进行处理,处理完成后交给下一个阶段的对象进行处理,最后完成整个处理过程。

ConnectionExpirerThread

该类的主要任务是从ExpiryQueue cnxnExpiryQueue这个终止队列中获取已经终止的session,并对这些session和连接进行关闭。
该类继承了ZooKeeperThread类,所以实现代码都在run()函数中。

关键代码如下:

 public void run() {
 ...
 for (NIOServerCnxn conn : cnxnExpiryQueue.poll()) {
                        conn.close();
                    }
                    
...
}

总结

本文讲述了zookeeper中的关键组件NIOServerCnxnFactory类的启动运行过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值