zookeeper source code analysis

Zookeeper-jute分析

本章将对Zookeeper项目中的zookeeper-jute模块进行分析,在Zookeeper项目中zookeeper-jute模块主要承担的责任是序列化与反序列化,此外还有部分核心数据结构的定义。

Zookeeper-jute概述

在Zookeeper项目中将序列化与反序列化相关内容都存放在zookeeper-jute模块中,本节将对该模块中的基础内容做简单概述。接下来将编写一个Jute相关的测试用例,第一步需要实现Record接口,具体代码如下。

// 省略GET、SET和构造函数
public class DemoRecord implements Record {
  private String name;
  private int age;

  @Override
  public void serialize(OutputArchive archive, String tag) throws IOException {
    archive.startRecord(this, tag);
    archive.writeInt(age, "age");
    archive.writeString(name, "name");
    archive.endRecord(this, tag);

  }

  @Override
  public void deserialize(InputArchive archive, String tag) throws IOException {
    archive.startRecord(tag);
    this.age = archive.readInt("age");
    this.name = archive.readString("name");
    archive.endRecord(tag);

  }
}

在上述代码中定义了两个成员变量分别是。

  1. 成员变量name表示名称。
  2. 成员变量age表示年龄。

在上述代码中需要重点关注Record接口的实现,先对serialize方法进行说明,具体处理流程如下。

  1. 将输出档案标记为开始。
  2. 写入tag名称为age的数据,数据值为成员变量age的数据值。
  3. 写入tag名称为name的数据,数据值为成员变量name的数据值。
  4. 将输出档案标记为结束。

其次对deserialize方法进行说明,具体处理流程如下。

  1. 将输入档案标记为开始。
  2. 从输入档案中图区tag名称为age的数据将其赋值到成员变量age。
  3. 从输入档案中图区tag名称为name的数据将其赋值到成员变量name。
  4. 将输入档案标记位结束。

准备完成Record实现类以后进行使用用例的编写具体代码如下。

public static void main(String[] args) throws Exception {

  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  OutputArchive boa = BinaryOutputArchive.getArchive(baos);
  DemoRecord zhangsan = new DemoRecord("zhangsan", 10);
  zhangsan.serialize(boa, "data1");

  ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  InputArchive bia = BinaryInputArchive.getArchive(bais);
  DemoRecord demoRecord = new DemoRecord();
  demoRecord.deserialize(bia, "data1");



  baos.close();
  bais.close();
}

在上述代码中核心处理流程如下。

  1. 创建字节输出流,将字节输出流作为参数创建输出档案。
  2. 创建DemoRecord对象,将其序列化到输出档案中。
  3. 创建字节输入流,将字节输入流作为参数创建输入档案。
  4. 通过输入档案将数据值序列化到demoRecord变量中。

在上述流程处理中需要注意在第(3)步中需要将字节输出流中的内容作为参数来创建字节输入流,上述代码通过调试可以发现第(2)步中序列化到输出档案中的内容通过第(3)(4)步将数据反序列化到Java内存中,具体存储变量是demoRecord,信息如图所示。

image-20220302110732921

从jute的使用中可以发现核心接口有三个,分别是。

  1. 输入档案InputArchive。
  2. 输出档案OutputArchive。
  3. 序列化和反序列化接口Record,注意核心能力在输入档案和输出档案中。

InputArchive和OutputArchive

在Zookeeper项目中关于档案的存储或者传输形式有三种分别是。

  1. 以XML表示的输入档案XmlInputArchive和输出档案XmlOutputArchive。
  2. 以CSV表示的输入档案CsvInputArchive和输出档案CsvOutputArchive。
  3. 以二进制表示的输入档案BinaryInputArchive和输出档案BinaryOutputArchive。

上述三种传输形式使用最多的是二进制传输,接下来将对BinaryInputArchive类和BinaryOutputArchive类进行分析,首先对BinaryOutputArchive类进行说明,先看构造函数,具体代码如下。

public BinaryOutputArchive(DataOutput out) {
  this.out = out;
}

在这段构造函数中需要使用到DataOutput接口,处理是将其设置到成员变量out中,该构造函数使用的较少,通常想要构造BinaryOutputArchive类的实例会采用静态方法getArchive,具体代码如下。

public static BinaryOutputArchive getArchive(OutputStream strm) {
  return new BinaryOutputArchive(new DataOutputStream(strm));
}

在上述代码中会将输出流专为DataOutputStream类型的输出流并调用构造函数完成BinaryOutputArchive类的初始化。当拥有成员变量out以后就可以进行数据写出操作,下面举例说明布尔值的写入,具体代码如下。

public void writeBool(boolean b, String tag) throws IOException {
  out.writeBoolean(b);
}

从上述代码中可以发现写出一个布尔值操作就是调用DataOutput接口提供的writeBoolean方法。其他类型的写出操作不做具体分析。

其次对BinaryInputArchive类进行分析,同样先看构造函数,具体代码如下。

public BinaryInputArchive(DataInput in, int maxBufferSize, int extraMaxBufferSize) {
  this.in = in;
  this.maxBufferSize = maxBufferSize;
  this.extraMaxBufferSize = extraMaxBufferSize;
}

在这个构造函数中存在三个变量它们的含义如下。

  1. 变量in表示数据输入。
  2. 变量maxBufferSize表示最大缓冲区大小。
  3. 变量extraMaxBufferSize表示额外的最大缓冲区大小。

同样的该构造函数的使用频率也比较低,通常会采用静态方法getArchive来获取实例,具体代码如下。

static public BinaryInputArchive getArchive(InputStream strm) {
  return new BinaryInputArchive(new DataInputStream(strm));
}

接下来查看与writeBool方法对应的readBool方法,具体处理代码如下。

public boolean readBool(String tag) throws IOException {
    return in.readBoolean();
}

在这段代码中通过成员变量in将布尔值进行读取,并将读取结果返回。在BinaryInputArchive类中还有其他read方法操作均为成员变量in的函数调用不做具体分析。

Zookeeper核心数据结构

在zookeeper-jute模块中除了序列化与反序列化相关内容外还有部分数据结构的定义,具体定义内容在zookeeper-jute/src/main/resources/zookeeper.jute文件中,后续会对该文件中的内容进行概要说明,在此之前需要了解Zookeeper项目中的公共数据属性含义。

  1. zxid表示全局唯一事务ID。
  2. czxid表示节点创建时的zxid。
  3. mzxid表示节点修改时的zxid,当前节点修改数据后会更新该数据。
  4. ctime表示节点创建的时间,当前节点创建时数据初始化。
  5. mtime表示节点修改的时间,当前节点修改数据后会更新该数据。
  6. version表示当前节点版本号,当前节点数据修改后会进行修改,修改行为是累加1。
  7. cversion表示直接子节点版本号,直接子节点创建、删除、修改都会修改当前数据,修改行为是累加1。
  8. aversion表示acl版本号。
  9. ephemeralOwner表示创建该临时节点是的会话ID,如果该节点是持久化节点数据值为0。
  10. dataLength表示数据长度。
  11. numChildren表示直接子节点数量。
  12. pzxid表示 直接子节点最后跟新的zxid,直接子节点创建、删除、修改都会修改当前数据。

在了解上述公共数据属性的含义以后对绝大部分的数据定义都可以理解,接下来主要对org.apache.zookeeper.data包下的内容进行说明,在该包下存在四个类分别是Id、ACL、Stat和StatPersisted,着重关注Id和ACL类,Id类的成员变量表见表

  • Id成员变量表
变量名称变量类型变量说明
idString序号
schemeString方案

在Id的成员变量中着重关注scheme变量,它有四种候选数据。

  1. world表示开放模式,即对访问不做限制。
  2. ip表示ip模式仅限允许的ip进行访问。
  3. auth表示用户认证模式,只有通过用户认证的客户端才可以访问。
  4. digest表示用户认证模式,与auth的差异是digest模式会进行密码加密,auth模式密码是明文传输。

了解Id的成员变量后下面对ACL成员变量进行说明,详细内容见表

  • ACL成员变量表
变量名称变量类型变量说明
idorg.apache.zookeeper.data.IdId
permsInt权限编号

在ACL的成员变量中需要重点关注perms变量,它有六种候选数据,具体存储在org.apache.zookeeper.ZooDefs.Perms类中。

  1. 1表示READ,读权限。
  2. 2表示WRITE,写权限。
  3. 4表示CREATE,创建权限。
  4. 8表示DELETE,删除权限。
  5. 16表示ADMIN,管理者权限,拥有ADMIN权限可以设置ACL权限。
  6. 31表示上述权限都具备。

总结

本站着重对Zookeeper项目中的jute模块进行分析,首先对jute模块的使用做简单介绍,然后由简单使用中所用到的关键接口InputArchive和OutputArchive做相关分析,最后对Zookeeper项目中的核心数据结构做介绍.