Zookeeper序列化及通信协议

      在了解Zookeeper值怎么实现网络之间的消息通信时候,我们先要了解一下Zookeeper是怎么进行消息序列化和发序列化的;

一、前言

     前面介绍了Zookeeper的系统模型,下面进一步学习Zookeeper的底层序列化机制,Zookeeper的客户端与服务端之间会进行一系列的网络通信来实现数据传输,Zookeeper使用Jute组件来完成数据的序列化和反序列化操作。

二、Jute

   Jute是Zookeeper底层序列化组件,其用于Zookeeper进行网络数据传输和本地磁盘数据存储的序列化和反序列化工作。

MockReHeader实体类

 public class MockReHeader implements Record { 
     private long sessionId;    
     private String type;    
    public MockReHeader() {  
        
    }    
    
    public MockReHeader(long sessionId, String type) {       
        this.sessionId = sessionId;        
        this.type = type;
    }  
      
    public void setSessionId(long sessionId) {        
          this.sessionId = sessionId;
    }   
     
    public void setType(String type) { 
        this.type = type;
    }  
      
    public long getSessionId() {  
        return sessionId;
    } 
       
    public String getType() {       
        return type;
    }  
      
    public void serialize(OutputArchive oa, String tag) throws java.io.IOException {
        oa.startRecord(this, tag);
        oa.writeLong(sessionId, "sessionId");
        oa.writeString(type, "type");
        oa.endRecord(this, tag);
    } 
       
    public void deserialize(InputArchive ia, String tag) throws java.io.IOException {
        ia.startRecord(tag);        
        this.sessionId = ia.readLong("sessionId");        
        this.type = ia.readString("type");
        ia.endRecord(tag);
    }
    
    @Override    public String toString() {        
          return "sessionId = " + sessionId + ", type = " + type;
    }
}

Main  

public class Main {    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos);        
        new MockReHeader(0x3421eccb92a34el, "ping").serialize(boa, "header");
        
        ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray());
        
        ByteBufferInputStream bbis = new ByteBufferInputStream(bb);
        BinaryInputArchive bia = BinaryInputArchive.getArchive(bbis);
        
        MockReHeader header2 = new MockReHeader();
        System.out.println(header2);
        header2.deserialize(bia, "header");
        System.out.println(header2);
        bbis.close();
        baos.close();    
    }
}

运行结果

sessionId = 0, type = null
sessionId = 14673999700337486, type = ping

     说明:可以看到MockReHeader实体类需要实现Record接口并且实现serialize和deserialize方法。

               OutputArchive和InputArchive分别是Jute底层的序列化器和反序列化器。

     在Zookeeper的src文件夹下有zookeeper.jute文件,其内容如下  

blob.png

    其定义了所有的实体类的所属包名、类名及类的所有成员变量和类型,该文件会在源代码编译时,Jute会使用不同的代码生成器为这些类定义生成实际编程语言的类文件,如java语言生成的类文件保存在src/java/generated目录下,每个类都会实现Record接口。

三、通信协议

  基于TCP/IP协议,Zookeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信,对于请求,主要包含请求头和请求体,对于响应,主要包含响应头和响应体。

blob.png

3.1 请求协议

  对于请求协议而言,如下为获取节点数据请求的完整协议定义

blob.png

class RequestHeader {        
       int xid;      
       int type;
   }

     从zookeeper.jute中可知RequestHeader包含了xid和type,xid用于记录客户端请求发起的先后序号,用来确保单个客户端请求的响应顺序,type代表请求的操作类型,如创建节点(OpCode.create)、删除节点(OpCode.delete)、获取节点数据(OpCode.getData)。    

协议的请求主体内容部分,包含了请求的所有操作内容,不同的请求类型请求体不同。对于会话创建而言,其请求体如下

1.      class ConnectRequest {  
2.          int protocolVersion;  
3.          long lastZxidSeen;   //客户端保存ZXID
4.          int timeOut;  //会话超时时间
5.          long sessionId;  
6.          buffer passwd;  
7.      }

      Zookeeper客户端和服务器在创建会话时,会发送ConnectRequest请求,该请求包含协议版本号protocolVersion、最近一次接收到服务器ZXID lastZxidSeen、会话超时时间timeOut、会话标识sessionId和会话密码passwd。


  对于获取节点数据而言,其请求体如下  

1.      class GetDataRequest {  
2.          ustring path;  
3.          boolean watch;  
4.      }

     Zookeeper客户端在向服务器发送节点数据请求时,会发送GetDataRequest请求,该请求包含了数据节点路径path、是否注册Watcher的标识watch。

 

   对于更新节点数据而言,其请求体如下  

1.      class SetDataRequest {  
2.          ustring path;  
3.          buffer data;  
4.          int version;  
5.      }

     Zookeeper客户端在向服务器发送更新节点数据请求时,会发送SetDataRequest请求,该请求包含了数据节点路径path、数据内容data、节点数据的期望版本号version。


针对不同的请求类型,Zookeeper都会定义不同的请求体,可以在zookeeper.jute中查看。

 3.2 响应协议

  对于响应协议而言,如下为获取节点数据响应的完整协议定义

blob.png

 响应头中包含了每个响应最基本的信息,包括xid、zxid和err: 

1.      class ReplyHeader {  
2.          int xid;  
3.          long zxid;  
4.          int err;  
5.      }

 xid与请求头中的xid一致,zxid表示Zookeeper服务器上当前最新的事务ID,err则是一个错误码,表示当请求处理过程出现异常情况时,就会在错误码中标识出来,常见的包括处理成功(Code.OK)、节点不存在(Code.NONODE)、没有权限(Code.NOAUTH)。

 协议的响应主体内容部分,包含了响应的所有数据,不同的响应类型请求体不同。对于会话创建而言,其响应体如下  

class ConnectResponse {       
       int protocolVersion;    
        int timeOut;      
        long sessionId;
        buffer passwd;
   }

  针对客户端的会话创建请求,服务端会返回客户端一个ConnectResponse响应,该响应体包含了版本号protocolVersion、会话的超时时间timeOut、会话标识sessionId和会话密码passwd。

  对于获取节点数据而言,其响应体如下 

    class GetDataResponse {
       buffer data;
       org.apache.zookeeper.data.Stat stat;
   }

 针对客户端的获取节点数据请求,服务端会返回客户端一个GetDataResponse响应,该响应体包含了数据节点内容data、节点状态stat。

 对于更新节点数据而言,其响应体如下  

    class SetDataResponse {
       org.apache.zookeeper.data.Stat stat;
   }

  针对客户端的更新节点数据请求,服务端会返回客户端一个SetDataResponse响应,该响应体包含了最新的节点状态stat。

  针对不同的响应类型,Zookeeper都会定义不同的响应体,可以在zookeeper.jute中查看。

四、总结

  本篇博客讲解了Zookeeper中的序列化机制和客户端与服务端、服务端与服务端的通信协议,内容相对较为简单,容易理解,谢谢各位园友的观看~

发表评论