Zookeeper (一) 架设源码调试环境

2,412 阅读4分钟

前言

ZooKeeper 是一个分布式协调中间件,本质上说,应该是一个存储+订阅的中间件。它的 API 还是比较简单的,使用的时候一般有 ZKClient 用于和远端的 ZKServer 通信,API 上一般是对节点进行增删查改,由于 ZKClient 和 ZKServer 的长连接,使 ZooKeeper 还能向服务提供 Watch 等订阅节点动态的效果。

简而言之,ZooKeeper 的通信和选举还是很值得看看的,特别是它还能作为注册中心让其他服务使用。

ZooKeeper 源码下载和调试

GitHub 下载源码

GitHub 直接下载源码会比较慢。解决方法在这里借助 Gitee 解决 Github 拉取源码过慢的问题 (推荐使用域名修改的方法进行加速)

IDEA 导入源码

IDEA 导入源码,ZooKeeper 有很多博客是介绍如何用 ant 进行源码环境调试的,比较新的 ZooKeeper 版本都已经迁移到 maven 上了。所以直接下载下来,用 maven 进行依赖下载即可。推荐使用 ali 的 maven 远程仓库。

单机模式调试

1 拷贝配置文件

在 conf/zoo_sample.cfg 拷贝 一份到自定义目录。这里我直接拷贝到当前conf目录下,并命名为 zoo1.cfg

# 只选取关键的配置
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper/z1/data   # 创建好data目录
dataLogDir=/tmp/zookeeper/z1/log # 定义好日志文件
clientPort=2181

1.2 拷贝 log4j.properties

conf/log4j.properties 的日志配置拷贝一份到 zookeeper/zookeeper-server/src/main/resources 。这样才能打印出更多的日志提供调试。

2020-05-09 补充 拷贝日志配置 thx 微信网友: 在路上

2 寻找 ZooKeeper Server 的启动主类

我们都知道,ZooKeeper 平时是使用 zkServer.sh 启动的。查看一下这个 shell 脚本

start)
    echo  -n "Starting zookeeper ... "
    if [ -f "$ZOOPIDFILE" ]; then
      if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then
         echo $command already running as process `cat "$ZOOPIDFILE"`.
         exit 1
      fi
    fi
    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN 这里标记着 ZOOMAIN 方法 "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
    
    ZOOMAIN ZOOCFG 为参数
    .... 
    
    ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only
        =$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain"
    
    从此处可以知道,QuorumPeerMain 类就是 ZooKeeper Server 的启动类了。 
  

从 shell 文件中看到,QuorumPeerMain 类就是 ZooKeeper Server 的启动类了。

定义 Run/Debug Configurations,定义 QuorumPeerMain 是执行main方法的类,并把配置文件文件的路径作为参数写入去。

执行 Debug 定义完后,就可以直接 Debug 程序了。

这里注意需要 勾选 include dependencies with "Provided" scope,pom.xml 有几个依赖是标记为 Provided 的,编译不会有问题,但是启动时会找不到部分依赖的类。

图片源自 网上博客

// QuorumPeerMain.java#initializeAndRun()
        if (args.length == 1 && config.isDistributed()) {
            // 假如是集群模式,走 QuorumPeerMain 的 runFromConfig
            runFromConfig(config);
        } else {
            LOG.warn("Either no config or no quorum defined in config, running in standalone mode");
            // there is only server in the quorum -- run as standalone
            // 假如是单机模式,则转发为 ZooKeeperServerMain 直接启动
            ZooKeeperServerMain.main(args);
        }

也就是说 ZooKeeperServerMain 是可以启动的,但无法启动集群模式。在调试集群模式时,使用 ZooKeeperServerMain 引用config文件,一直都是 standalone 模式,在此提醒各位读者,别走弯路。

ZooKeeper 集群部署调试

以前是启动单个 ZooKeeper 的方法,基于上面的步骤,启动一个集群模式来调试也是非常简单的。网上有很多相关的博客,启动三个节点的 ZK集群 基本步骤如下:

  1. 定义三个目录存放节点数据,并定义 myid 进程文件
  2. 定义三个配置文件,并在配置中增加如下配置:
admin.serverPort=8080 # 这个是ZooKeeper开发的 http 端口

# 集群配置
server.1=127.0.0.1:2222:2223
server.2=127.0.0.1:3333:3334
server.3=127.0.0.1:4444:4445
  1. 按照启动单机的方法,逐个启动节点即可。
  2. 观察输出,使用 zkClient.sh 观察验证集群有效。

这部分详细可以参考 引用2

后记

部署 ZooKeeper 还是比较简单的,这么看以前一直觉得高大上的中间件,其实都可以一点点地去学习、调试、熟悉、修改、打包部署。这些框架并没有那么神秘,继续学习基础,积累底层的通信、并发、操作系统、计算机网络、数据结构,这样会帮助我们快速地熟悉这些开源的项目源码。

后面会努力调试 ZooKeeper 以下相关的细节:

  1. ZooKeeper 集群下的选举过程
  2. ZooKeeper 集群下的存储数据的过程,关于一致性的实现
  3. ZooKeeper 作为存储中间件,存储的结构是如何实现的

引用

  1. ZooKeeper启动之QuorumPeerMain
  2. ZooKeeper源码分析(四) ----- 集群模式(replicated)运行
  3. 《ZooKeeper 分布式过程协同技术详解》 Flavio Junqueira Benjamin Reed 著

补充更新

  • 2020-05-09 自动生成代码

Question: 群里有个同学表示找不到这些依赖

其实这些类是编译产生的,需要用 mvn compile 生成。IDE 假如不会自动导入,就需要收到设置这些类可以被读取编译。

  • 2020-07-16

Thanks 掘金@北兮

使用过程中遇到一个问题, 记录下关于 log4j.properties 没有作用 , 需要在 idea 中 File -> project structure -> Project Settings -> Modules -> zookeeper 将 src/main/resources 目录 mark as Resources 标记为资源文件