zoukankan      html  css  js  c++  java
  • Thrift源码分析(二)-- 协议和编解码

    协议和编解码是一个网络应用程序的核心问题之一,客户端和服务器通过约定的协议来传输消息(数据),通过特定的格式来编解码字节流,并转化成业务消息,提供给上层框架调用。

    Thrift的协议比较简单,它把协议和编解码整合在了一起。抽象类TProtocol定义了协议和编解码的顶层接口。个人感觉采用抽象类而不是接口的方式来定义顶层接口并不好,TProtocol关联了一个TTransport传输对象,而不是提供一个类似getTransport()的接口,导致抽象类的扩展性比接口差。

    TProtocol主要做了两个事情:

    1. 关联TTransport对象

    2.定义一系列读写消息的编解码接口,包括两类,一类是复杂数据结构比如readMessageBegin, readMessageEnd,  writeMessageBegin, writMessageEnd.还有一类是基本数据结构,比如readI32, writeI32, readString, writeString

    [java] view plain copy
     
    1. public abstract class TProtocol {  
    2.   
    3.   /** 
    4.    * Transport 
    5.    */  
    6.   protected TTransport trans_;  
    7.   
    8.  public abstract void writeMessageBegin(TMessage message) throws TException;  
    9.   
    10.   public abstract void writeMessageEnd() throws TException;  
    11.   
    12.   public abstract void writeStructBegin(TStruct struct) throws TException;  
    13.   
    14.   public abstract void writeStructEnd() throws TException;  
    15.   
    16.   public abstract void writeFieldBegin(TField field) throws TException;  
    17.   
    18.   public abstract void writeFieldEnd() throws TException;  
    19.   
    20.   public abstract void writeFieldStop() throws TException;  
    21.   
    22.   public abstract void writeMapBegin(TMap map) throws TException;  
    23.   
    24.   public abstract void writeMapEnd() throws TException;  
    25.   
    26.   public abstract void writeListBegin(TList list) throws TException;  
    27.   
    28.   public abstract void writeListEnd() throws TException;  
    29.   
    30.   public abstract void writeSetBegin(TSet set) throws TException;  
    31.   
    32.   public abstract void writeSetEnd() throws TException;  
    33.   
    34.   public abstract void writeBool(boolean b) throws TException;  
    35.   
    36.   public abstract void writeByte(byte b) throws TException;  
    37.   
    38.   public abstract void writeI16(short i16) throws TException;  
    39.   
    40.   public abstract void writeI32(int i32) throws TException;  
    41.   
    42.   public abstract void writeI64(long i64) throws TException;  
    43.   
    44.   public abstract void writeDouble(double dub) throws TException;  
    45.   
    46.   public abstract void writeString(String str) throws TException;  
    47.   
    48.   public abstract void writeBinary(ByteBuffer buf) throws TException;  
    49.   
    50.   /** 
    51.    * Reading methods. 
    52.    */  
    53.   
    54.   public abstract TMessage readMessageBegin() throws TException;  
    55.   
    56.   public abstract void readMessageEnd() throws TException;  
    57.   
    58.   public abstract TStruct readStructBegin() throws TException;  
    59.   
    60.   public abstract void readStructEnd() throws TException;  
    61.   
    62.   public abstract TField readFieldBegin() throws TException;  
    63.   
    64.   public abstract void readFieldEnd() throws TException;  
    65.   
    66.   public abstract TMap readMapBegin() throws TException;  
    67.   
    68.   public abstract void readMapEnd() throws TException;  
    69.   
    70.   public abstract TList readListBegin() throws TException;  
    71.   
    72.   public abstract void readListEnd() throws TException;  
    73.   
    74.   public abstract TSet readSetBegin() throws TException;  
    75.   
    76.   public abstract void readSetEnd() throws TException;  
    77.   
    78.   public abstract boolean readBool() throws TException;  
    79.   
    80.   public abstract byte readByte() throws TException;  
    81.   
    82.   public abstract short readI16() throws TException;  
    83.   
    84.   public abstract int readI32() throws TException;  
    85.   
    86.   public abstract long readI64() throws TException;  
    87.   
    88.   public abstract double readDouble() throws TException;  
    89.   
    90.   public abstract String readString() throws TException;  
    91.   
    92.   public abstract ByteBuffer readBinary() throws TException;  
    93.   
    94.   /** 
    95.    * Reset any internal state back to a blank slate. This method only needs to 
    96.    * be implemented for stateful protocols. 
    97.    */  
    98.   public void reset() {}  
    99.     
    100.   /** 
    101.    * Scheme accessor 
    102.    */  
    103.   public Class<? extends IScheme> getScheme() {  
    104.     return StandardScheme.class;  
    105.   }  
    106.   
    107. }  


    所谓协议就是客户端和服务器端约定传输什么数据,如何解析传输的数据。对于一个RPC调用的协议来说,要传输的数据主要有:

    调用方

    1. 方法的名称,包括类的名称和方法的名称

    2. 方法的参数,包括类型和参数值

    3.一些附加的数据,比如附件,超时事件,自定义的控制信息等等

    返回方

    1. 调用的返回码

    2. 返回值

    3.异常信息

    从TProtocol的定义我们可以看出Thrift的协议约定如下事情:

    1. 先writeMessageBegin表示开始传输消息了,写消息头。Message里面定义了方法名,调用的类型,版本号,消息seqId

    2. 接下来是写方法的参数,实际就是写消息体。如果参数是一个类,就writeStructBegin

    3. 接下来写字段,writeFieldBegin, 这个方法会写接下来的字段的数据类型和顺序号。这个顺序号是Thrfit对要传输的字段的一个编码,从1开始

    4. 如果是一个集合就writeListBegin/writeMapBegin,如果是一个基本数据类型,比如int, 就直接writeI32

    5. 每个复杂数据类型写完都调用writeXXXEnd,直到writeMessageEnd结束

    6. 读消息时根据数据类型读取相应的长度

    每个writeXXX都是采用消息头+消息体的方式。我们来看TBinaryProtocol的实现。

    1. writeMessgeBegin方法写了消息头,包括4字节的版本号和类型信息,字符串类型的方法名,4字节的序列号seqId

    2. writeFieldBegin,写了1个字节的字段数据类型,和2个字节字段的顺序号

    3. writeI32,写了4个字节的字节数组

    4. writeString,先写4字节消息头表示字符串长度,再写字符串字节

    5. writeBinary,先写4字节消息头表示字节数组长度,再写字节数组内容

    6.readMessageBegin时,先读4字节版本和类型信息,再读字符串,再读4字节序列号

    7.readFieldBegin,先读1个字节的字段数据类型,再读2个字节的字段顺序号

    8. readString时,先读4字节字符串长度,再读字符串内容。字符串统一采用UTF-8编码

    [java] view plain copy
     
    1.  public void writeMessageBegin(TMessage message) throws TException {  
    2.     if (strictWrite_) {  
    3.       int version = VERSION_1 | message.type;  
    4.       writeI32(version);  
    5.       writeString(message.name);  
    6.       writeI32(message.seqid);  
    7.     } else {  
    8.       writeString(message.name);  
    9.       writeByte(message.type);  
    10.       writeI32(message.seqid);  
    11.     }  
    12.   }  
    13.   
    14. public void writeFieldBegin(TField field) throws TException {  
    15.     writeByte(field.type);  
    16.     writeI16(field.id);  
    17.   }  
    18.   
    19. private byte[] i32out = new byte[4];  
    20.   public void writeI32(int i32) throws TException {  
    21.     i32out[0] = (byte)(0xff & (i32 >> 24));  
    22.     i32out[1] = (byte)(0xff & (i32 >> 16));  
    23.     i32out[2] = (byte)(0xff & (i32 >> 8));  
    24.     i32out[3] = (byte)(0xff & (i32));  
    25.     trans_.write(i32out, 0, 4);  
    26.   }  
    27.   
    28. public void writeString(String str) throws TException {  
    29.     try {  
    30.       byte[] dat = str.getBytes("UTF-8");  
    31.       writeI32(dat.length);  
    32.       trans_.write(dat, 0, dat.length);  
    33.     } catch (UnsupportedEncodingException uex) {  
    34.       throw new TException("JVM DOES NOT SUPPORT UTF-8");  
    35.     }  
    36.   }  
    37.   
    38. public void writeBinary(ByteBuffer bin) throws TException {  
    39.     int length = bin.limit() - bin.position();  
    40.     writeI32(length);  
    41.     trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);  
    42.   }  
    43.   
    44. public TMessage readMessageBegin() throws TException {  
    45.     int size = readI32();  
    46.     if (size < 0) {  
    47.       int version = size & VERSION_MASK;  
    48.       if (version != VERSION_1) {  
    49.         throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");  
    50.       }  
    51.       return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());  
    52.     } else {  
    53.       if (strictRead_) {  
    54.         throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");  
    55.       }  
    56.       return new TMessage(readStringBody(size), readByte(), readI32());  
    57.     }  
    58.   }  
    59.   
    60. public TField readFieldBegin() throws TException {  
    61.     byte type = readByte();  
    62.     short id = type == TType.STOP ? 0 : readI16();  
    63.     return new TField("", type, id);  
    64.   }  
    65.   
    66. public String readString() throws TException {  
    67.     int size = readI32();  
    68.   
    69.     if (trans_.getBytesRemainingInBuffer() >= size) {  
    70.       try {  
    71.         String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8");  
    72.         trans_.consumeBuffer(size);  
    73.         return s;  
    74.       } catch (UnsupportedEncodingException e) {  
    75.         throw new TException("JVM DOES NOT SUPPORT UTF-8");  
    76.       }  
    77.     }  
    78.   
    79.     return readStringBody(size);  
    80.   }  

    TProtocol定义了基本的协议信息,包括传输什么数据,如何解析传输的数据的基本方法。

    还存在一个问题,就是服务器端如何知道客户端发送过来的数据是怎么组合的,比如第一个字段是字符串类型,第二个字段是int。这个信息是在IDL生成客户端时生成的代码时提供了。Thrift生成的客户端代码提供了读写参数的方法,这两个方式是一一对应的,包括字段的序号,类型等等。客户端使用写参数的方法,服务器端使用读参数的方法。

    关于IDL生成的客户端代码会在后面的文章具体描述。下面简单看一下自动生成的代码

    1. 方法的调用从writeMessageBegin开始,发送了消息头信息

    2. 写方法的参数,也就是写消息体。方法参数由一个统一的接口TBase描述,提供了read和write的统一接口。自动生成的代码提供了read, write方法参数的具体实现

    3. 写完结束  

    [java] view plain copy
     
    1.  public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {  
    2.         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("handle", org.apache.thrift.protocol.TMessageType.CALL, 0));  
    3.         handle_args args = new handle_args();  
    4.         args.setIdentity(identity);  
    5.         args.setUid(uid);  
    6.         args.setSid(sid);  
    7.         args.setType(type);  
    8.         args.setMessage(message);  
    9.         args.setParams(params);  
    10.         args.write(prot);  
    11.         prot.writeMessageEnd();  
    12.       }  
    13.   
    14. public interface TBase<T extends TBase<?,?>, F extends TFieldIdEnum> extends Comparable<T>,  Serializable {  
    15.   
    16.   public void read(TProtocol iprot) throws TException;  
    17.   
    18.   public void write(TProtocol oprot) throws TException;  
    19. }  
    20.   
    21. public static class handle_args <strong>implements org.apache.thrift.TBase</strong><handle_args, handle_args._Fields>, java.io.Serializable, Cloneable   {  
    22.     private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("handle_args");  
    23.   
    24.     private static final org.apache.thrift.protocol.TField IDENTITY_FIELD_DESC = new org.apache.thrift.protocol.TField("identity", org.apache.thrift.protocol.TType.STRING, (short)1);  
    25.     private static final org.apache.thrift.protocol.TField UID_FIELD_DESC = new org.apache.thrift.protocol.TField("uid", org.apache.thrift.protocol.TType.I64, (short)2);  
    26.     private static final org.apache.thrift.protocol.TField SID_FIELD_DESC = new org.apache.thrift.protocol.TField("sid", org.apache.thrift.protocol.TType.STRING, (short)3);  
    27.     private static final org.apache.thrift.protocol.TField TYPE_FIELD_DESC = new org.apache.thrift.protocol.TField("type", org.apache.thrift.protocol.TType.I32, (short)4);  
    28.     private static final org.apache.thrift.protocol.TField MESSAGE_FIELD_DESC = new org.apache.thrift.protocol.TField("message", org.apache.thrift.protocol.TType.STRING, (short)5);  
    29.     private static final org.apache.thrift.protocol.TField PARAMS_FIELD_DESC = new org.apache.thrift.protocol.TField("params", org.apache.thrift.protocol.TType.MAP, (short)6);  
    30.   
    31.     private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();  
    32.     static {  
    33.       schemes.put(StandardScheme.class, new handle_argsStandardSchemeFactory());  
    34.       schemes.put(TupleScheme.class, new handle_argsTupleSchemeFactory());  
    35.     }  
    36.   
    37.     public String identity; // required  
    38.     public long uid; // required  
    39.     public String sid; // required  
    40.     public int type; // required  
    41.     public String message; // required  
    42.     public Map<String,String> params; // required  
    43.   
    44.     /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */  
    45.     public enum _Fields implements org.apache.thrift.TFieldIdEnum {  
    46.       IDENTITY((short)1, "identity"),  
    47.       UID((short)2, "uid"),  
    48.       SID((short)3, "sid"),  
    49.       TYPE((short)4, "type"),  
    50.       MESSAGE((short)5, "message"),  
    51.       PARAMS((short)6, "params");  
    52. }  
    53.   
    54. //  自动生成的写方法参数的方法,按照字段顺序写,给客户端代码使用  
    55.       public void write(org.apache.thrift.protocol.TProtocol oprot, handle_args struct) throws org.apache.thrift.TException {  
    56.         struct.validate();  
    57.   
    58.         oprot.writeStructBegin(STRUCT_DESC);  
    59.         if (struct.identity != null) {  
    60.           oprot.writeFieldBegin(IDENTITY_FIELD_DESC);  
    61.           oprot.writeString(struct.identity);  
    62.           oprot.writeFieldEnd();  
    63.         }  
    64.         oprot.writeFieldBegin(UID_FIELD_DESC);  
    65.         oprot.writeI64(struct.uid);  
    66.         oprot.writeFieldEnd();  
    67.         if (struct.sid != null) {  
    68.           oprot.writeFieldBegin(SID_FIELD_DESC);  
    69.           oprot.writeString(struct.sid);  
    70.           oprot.writeFieldEnd();  
    71.         }  
    72.         oprot.writeFieldBegin(TYPE_FIELD_DESC);  
    73.         oprot.writeI32(struct.type);  
    74.         oprot.writeFieldEnd();  
    75.         if (struct.message != null) {  
    76.           oprot.writeFieldBegin(MESSAGE_FIELD_DESC);  
    77.           oprot.writeString(struct.message);  
    78.           oprot.writeFieldEnd();  
    79.         }  
    80. }  
    81.   
    82. <pre name="code" class="java">//  自动生成的读方法参数的方法,按照字段顺序读,给服务器端代码使用  


     public void read(org.apache.thrift.protocol.TProtocol iprot, handle_args struct) throws org.apache.thrift.TException {
            org.apache.thrift.protocol.TField schemeField;
            iprot.readStructBegin();
            while (true)
            {
              schemeField = iprot.readFieldBegin();
              if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
                break;
              }
              switch (schemeField.id) {
                case 1: // IDENTITY
                  if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                    struct.identity = iprot.readString();
                    struct.setIdentityIsSet(true);
                  } else { 
                    org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
                  }
                  break;
                case 2: // UID
                  if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
                    struct.uid = iprot.readI64();
                    struct.setUidIsSet(true);
                  } else { 
                    org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
                  }
                  break;
                case 3: // SID
                  if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                    struct.sid = iprot.readString();
                    struct.setSidIsSet(true);
                  } else { 
                    org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
                  }
                  break;
                case 4: // TYPE
                  if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
                    struct.type = iprot.readI32();
                    struct.setTypeIsSet(true);
                  } else { 
                    org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
                  }
                  break;

  • 相关阅读:
    Hard Rock
    Codeforces Round #416 (Div. 2) B. Vladik and Complicated Book
    codeforces 793B. Igor and his way to work
    codeforces 1B Spreadsheets
    HDU 1069 Monkey and Banana
    codeforces 2B The least round way
    【机器学习】 通俗说拟合
    python-八皇后问题
    python-核心知识思维导图
    python-@property 属性
  • 原文地址:https://www.cnblogs.com/heapStark/p/9218394.html
Copyright © 2011-2022 走看看