Apache ZooKeeper -从初始化到对外提供服务的过程解析( 单机模式 )

2021-08-17 16:28:56 浏览数 (1)

流程图


目的

通过对单机版的 ZooKeeper 中的启动与服务的初始化过程进行分析,来了解 ZooKeeper 服务端相关的处理知识。现在开始深入到服务器端看一看 ZooKeeper 是如何从初始化到对外提供服务的。


启动前的准备工作

在 ZooKeeper 服务的初始化之前,首先要对配置文件等信息进行解析和载入。也就是在真正开始服务的初始化之前需要对服务的相关参数进行准备,而 ZooKeeper 服务的准备阶段大体上可分为启动程序入口、zoo.cfg 配置文件解析、创建历史文件清理器等

QuorumPeerMain 类是 ZooKeeper 服务的启动接口,可以理解为 Java 中的 main 函数。 通常我们在控制台启动 ZooKeeper 服务的时候,输入 zkServer.cm 或 zkServer.sh 命令就是用来启动这个 Java 类的。如下代码所示,QuorumPeerMain 类函数只有一个 initializeAndRun 方法,是作用为所有 ZooKeeper 服务启动逻辑的入口。

代码语言:javascript复制
package org.apache.zookeeper.server.quorum

public class QuorumPeerMain {

  ...

  public static void main(String[] args) {

  ...

    main.initializeAndRun(args);

  ...

  }

}

解析配置文件

首先要做的事情就是解析配置文件 zoo.cfg。我们知道zoo.cfg 是服务端的配置文件,在这个文件中可以配置数据目录、端口号等信息。

所以解析 zoo.cfg 配置文件是 ZooKeeper 服务启动的关键步骤。


创建文件清理器

面对大流量的网络访问,ZooKeeper 会因此产生海量的数据,如果磁盘数据过多或者磁盘空间不足,则会导致 ZooKeeper 服务器不能正常运行,进而影响整个分布式系统。

所以面对这种问题,ZooKeeper 采用了 DatadirCleanupManager 类作为历史文件的清理工具类。在 3.4.0 版本后的 ZooKeeper 中更是增加了自动清理历史数据的功能以尽量避免磁盘空间的浪费。

如下代码所示,DatadirCleanupManager 类有 5 个属性,其中 snapDir 和 dataLogDir 分别表示数据快照地址以及日志数据的存放地址。

在日常工作中可以通过在 zoo.cfg 文件中配置 autopurge.snapRetainCountautopurge.purgeInterval 这两个参数实现数据文件的定时清理功能,

  • autopurge.purgeInterval 这个参数指定了清理频率,以小时为单位,需要填写一个 1 或更大的整数,默认是 0,表示不开启自己清理功能。
  • autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目,默认是保留 3 个。
代码语言:javascript复制
public class DatadirCleanupManager {

    private final File snapDir;

    private final File dataLogDir;

    private final int snapRetainCount;

    private final int purgeInterval;

    private Timer timer;

}

服务初始化

经过了上面的配置文件解析等准备阶段后, ZooKeeper 开始服务的初始化阶段。初始化阶段可以理解为根据解析准备阶段的配置信息,实例化服务对象。服务初始化阶段的主要工作是创建用于服务统计的工具类,如下图所示主要有以下几种:

ServerStats创建

统计工具类 ServerStats用于统计 ZooKeeper 服务运行时的状态信息统计。主要统计的数据有服务端向客户端发送的响应包次数、接收到的客户端发送的请求包次数、服务端处理请求的延迟情况以及处理客户端的请求次数。在日常运维工作中,监控服务器的性能以及运行状态等参数很多都是这个类负责收集的。、

代码语言:javascript复制
public class ServerStats {

    private long packetsSent;

    private long packetsReceived;

    private long maxLatency;

    private long minLatency = Long.MAX_VALUE;

    private long totalLatency = 0;

    private long count = 0;

}

FileTxnSnapLog

用来管理 ZooKeeper 的数据存储等相关操作,可以看作为 ZooKeeper 服务层提供底层持久化的接口。在 ZooKeeper 服务启动过程中,它会根据 zoo.cfg 配置文件中的 dataDir 数据快照目录和 dataLogDir 事物日志目录来创建 FileTxnSnapLog 类。

代码语言:javascript复制
package org.apache.zookeeper.server.persistence;

public class FileTxnSnapLog {

  private final File dataDir;

  private final File snapDir;

  private TxnLog txnLog;

  private SnapShot snapLog;

  private final boolean autoCreateDB

}

ServerCnxnFactory

ZooKeeper 中客户端和服务端通过网络通信,其本质是通过 Java 的 IO 数据流的方式进行通信,但是传统的 IO 方式具有阻塞等待的问题,而 NIO 框架作为传统的 Java IO 框架的替代方案,在性能上大大优于前者。

也正因如此,NIO 框架也被广泛应用于网络传输的解决方案中。而 ZooKeeper 最早也是使用自己实现的 NIO 框架,但是从 3.4.0 版本后,引入了第三方 Netty 等框架来满足不同使用情况的需求,而我们可以通过 ServerCnxnFactory 类来设置 ZooKeeper 服务器,从而在运行的时候使用我们指定的 NIO 框架。如代码中 ServerCnxnFactory 类通过setServerCnxnFactory 函数来创建对应的工厂类

代码语言:javascript复制
package org.apache.zookeeper.server;

public abstract class ServerCnxnFactory {

final public void setZooKeeperServer(ZooKeeperServer zks) {

    this.zkServer = zks;

    if (zks != null) {

        if (secure) {

            zks.setSecureServerCnxnFactory(this);

        } else {

            zks.setServerCnxnFactory(this);

        }

    }

}

在通过 ServerCnxnFactory 类制定了具体的 NIO 框架类后。ZooKeeper 首先会创建一个线程 Thread 类作为 ServerCnxnFactory 类的启动主线程。之后 ZooKeeper 服务再初始化具体的 NIO 类。这里请你注意的是,虽然初始化完相关的 NIO 类 ,比如已经设置好了服务端的对外端口,客户端也能通过诸如 2181 端口等访问到服务端,但是此时 ZooKeeper 服务器还是无法处理客户端的请求操作。这是因为 ZooKeeper 启动后,还需要从本地的快照数据文件和事务日志文件中恢复数据。这之后才真正完成了 ZooKeeper 服务的启动。


初始化请求处理链

在完成了 ZooKeeper 服务的启动后,ZooKeeper 会初始化一个请求处理逻辑上的相关类。这个操作就是初始化请求处理链。

所谓的请求处理链是一种责任链模式的实现方式,根据不同的客户端请求,在 ZooKeeper 服务器上会采用不同的处理逻辑。

而为了更好地实现这种业务场景,ZooKeeper 中采用多个请求处理器类一次处理客户端请求中的不同逻辑部分。这种处理请求的逻辑方式就是责任链模式。而我们这里主要说的是单机版服务器的处理逻辑,主要分为PrepRequestProcessor、SyncRequestProcessor、FinalRequestProcessor 3 个请求处理器,而在一个请求到达 ZooKeeper 服务端进行处理的过程,则是严格按照这个顺序分别调用这 3 个类处理请求中的对应逻辑。


小结

主要从 ZooKeeper 服务内部的实现逻辑来学习 ZooKeeper 中的相关知识,从单机版服务器的启动,到对外提供服务的整个过程,逐步分析 ZooKeeper 实现的每个步骤,理解 ZooKeeper 服务器的初始化、配置解析、服务实例化等过程对我们日后在工作中分析排查 ZooKeeper 产生的相关问题以及提高 ZooKeeper 服务器的稳定性或性能都有很大的帮助。

0 人点赞