概诉
上一节,我们已经详解了IDL详细的语法,Thrift 就是根据这个语法,替我们生成目标代码的。下面我们来具体分析一下,生成的代码究竟是什么作用。
准备
目录结构
我们来看一下代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ├── java │ └── learn │ └── thrift │ ├── bean │ ├── client │ │ └── Client.java │ ├── constant │ │ └── ServerConfig.java │ ├── gen_code.sh │ ├── idl │ │ ├── bean.thrift │ │ └── hello.thrift │ ├── idlcode │ │ ├── HelloWorldService.java │ │ └── UserService.java │ └── server │ ├── Server.java │ └── handler │ ├── HelloWorldServiceImpl.java │ └── UserServiceImpl.java
代码生成
我们来看一下 gen_code.sh 的代码
1 2 3 4 5 6 7 8 9 #!/bin/zsh thrift_name=hello.thrift thrift_bean=bean.thrift pathroot=../.. echo "gen code " thrift --gen java -out $pathroot ./idl/$thrift_name thrift --gen java -out $pathroot ./idl/$thrift_bean echo "finsh"
当我们执行这个脚本的时候 会将 idl 的两个文件生成对应的代码。
来看一下 bean.thrift 文件,这里主要是定义的 thrift struct 文件。
1 2 3 4 5 6 7 namespace java learn.thrift.bean struct Friends { 1 : required i16 No }
我们执行后发现在bean 下多了一个文件叫Friends 的类。
1 2 3 4 5 6 ├── java │ └── learn │ └── thrift │ ├── bean |-- Friends.java
生成代码样例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 package learn.thrift.bean;public class Friends implements org .apache .thrift .TBase <Friends , Friends ._Fields >, java .io .Serializable , Cloneable { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Friends" ); private static final org.apache.thrift.protocol.TField NO_FIELD_DESC = new org.apache.thrift.protocol.TField("No" , org.apache.thrift.protocol.TType.I16, (short )1 ); private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); static { schemes.put(StandardScheme.class , new FriendsStandardSchemeFactory ()) ; schemes.put(TupleScheme.class , new FriendsTupleSchemeFactory ()) ; } public short No; public enum _Fields implements org.apache.thrift.TFieldIdEnum { NO((short )1 , "No" ); private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); static { for (_Fields field : EnumSet.allOf(_Fields.class )) { byName.put(field.getFieldName(), field); } } public static _Fields findByThriftId (int fieldId) { switch (fieldId) { case 1 : return NO; default : return null ; } } public static _Fields findByThriftIdOrThrow (int fieldId) { _Fields fields = findByThriftId(fieldId); if (fields == null ) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!" ); return fields; } public static _Fields findByName (String name) { return byName.get(name); } private final short _thriftId; private final String _fieldName; _Fields(short thriftId, String fieldName) { _thriftId = thriftId; _fieldName = fieldName; } public short getThriftFieldId () { return _thriftId; } public String getFieldName () { return _fieldName; } } private static final int __NO_ISSET_ID = 0 ; private BitSet __isset_bit_vector = new BitSet(1 ); public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class ) ; tmpMap.put(_Fields.NO, new org.apache.thrift.meta_data.FieldMetaData("No" , org.apache.thrift.TFieldRequirementType.REQUIRED, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I16))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Friends.class , metaDataMap ) ; } public Friends () { } public Friends ( short No) { this (); this .No = No; setNoIsSet(true ); } public Friends (Friends other) { __isset_bit_vector.clear(); __isset_bit_vector.or(other.__isset_bit_vector); this .No = other.No; } public Friends deepCopy () { return new Friends(this ); } @Override public void clear () { setNoIsSet(false ); this .No = 0 ; } public short getNo () { return this .No; } public Friends setNo (short No) { this .No = No; setNoIsSet(true ); return this ; } public void unsetNo () { __isset_bit_vector.clear(__NO_ISSET_ID); } public boolean isSetNo () { return __isset_bit_vector.get(__NO_ISSET_ID); } public void setNoIsSet (boolean value) { __isset_bit_vector.set(__NO_ISSET_ID, value); } public void setFieldValue (_Fields field, Object value) { switch (field) { case NO: if (value == null ) { unsetNo(); } else { setNo((Short)value); } break ; } } public Object getFieldValue (_Fields field) { switch (field) { case NO: return Short.valueOf(getNo()); } throw new IllegalStateException(); } public boolean isSet (_Fields field) { if (field == null ) { throw new IllegalArgumentException(); } switch (field) { case NO: return isSetNo(); } throw new IllegalStateException(); } @Override public boolean equals (Object that) { if (that == null ) return false ; if (that instanceof Friends) return this .equals((Friends)that); return false ; } public boolean equals (Friends that) { if (that == null ) return false ; boolean this_present_No = true ; boolean that_present_No = true ; if (this_present_No || that_present_No) { if (!(this_present_No && that_present_No)) return false ; if (this .No != that.No) return false ; } return true ; } @Override public int hashCode () { return 0 ; } public int compareTo (Friends other) { if (!getClass().equals(other.getClass())) { return getClass().getName().compareTo(other.getClass().getName()); } int lastComparison = 0 ; Friends typedOther = (Friends)other; lastComparison = Boolean.valueOf(isSetNo()).compareTo(typedOther.isSetNo()); if (lastComparison != 0 ) { return lastComparison; } if (isSetNo()) { lastComparison = org.apache.thrift.TBaseHelper.compareTo(this .No, typedOther.No); if (lastComparison != 0 ) { return lastComparison; } } return 0 ; } public _Fields fieldForId (int fieldId) { return _Fields.findByThriftId(fieldId); } public void read (org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { schemes.get(iprot.getScheme()).getScheme().read(iprot, this ); } public void write (org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { schemes.get(oprot.getScheme()).getScheme().write(oprot, this ); } @Override public String toString () { StringBuilder sb = new StringBuilder("Friends(" ); boolean first = true ; sb.append("No:" ); sb.append(this .No); first = false ; sb.append(")" ); return sb.toString(); } public void validate () throws org.apache.thrift.TException { } private void writeObject (java.io.ObjectOutputStream out) throws java.io.IOException { try { write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); } catch (org.apache.thrift.TException te) { throw new java.io.IOException(te); } } private void readObject (java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { try { __isset_bit_vector = new BitSet(1 ); read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); } catch (org.apache.thrift.TException te) { throw new java.io.IOException(te); } } private static class FriendsStandardSchemeFactory implements SchemeFactory { public FriendsStandardScheme getScheme () { return new FriendsStandardScheme(); } } private static class FriendsStandardScheme extends StandardScheme <Friends > { public void read (org.apache.thrift.protocol.TProtocol iprot, Friends 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 : if (schemeField.type == org.apache.thrift.protocol.TType.I16) { struct.No = iprot.readI16(); struct.setNoIsSet(true ); } else { org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break ; default : org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } iprot.readFieldEnd(); } iprot.readStructEnd(); if (!struct.isSetNo()) { throw new org.apache.thrift.protocol.TProtocolException("Required field 'No' was not found in serialized data! Struct: " + toString()); } struct.validate(); } public void write (org.apache.thrift.protocol.TProtocol oprot, Friends struct) throws org.apache.thrift.TException { struct.validate(); oprot.writeStructBegin(STRUCT_DESC); oprot.writeFieldBegin(NO_FIELD_DESC); oprot.writeI16(struct.No); oprot.writeFieldEnd(); oprot.writeFieldStop(); oprot.writeStructEnd(); } } private static class FriendsTupleSchemeFactory implements SchemeFactory { public FriendsTupleScheme getScheme () { return new FriendsTupleScheme(); } } private static class FriendsTupleScheme extends TupleScheme <Friends > { @Override public void write (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol oprot = (TTupleProtocol) prot; oprot.writeI16(struct.No); } @Override public void read (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol iprot = (TTupleProtocol) prot; struct.No = iprot.readI16(); struct.setNoIsSet(true ); } } }
代码详解
类图
下面我们会对上文的代码做详细分析。首先我们来看一下类图。
我们可以看到 Friends 是继承 TBase , TBase 又实现 TFieldIdEnum。
我们来看TBase 代码
基础数据结构
TBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface TBase <T extends TBase <?, ?>, F extends TFieldIdEnum > extends Comparable <T >, Serializable { void read (TProtocol var1) throws TException ; void write (TProtocol var1) throws TException ; F fieldForId (int var1) ; boolean isSet (F var1) ; Object getFieldValue (F var1) ; void setFieldValue (F var1, Object var2) ; TBase<T, F> deepCopy () ; void clear () ; } public interface TFieldIdEnum { short getThriftFieldId () ; String getFieldName () ; }
TBase 是 Thrift 的所有Type 类型的基类接口,其中定了几个比较重要的方法,比如说读写,克隆和清空。 而TFieldIdEnum 只是一个id 和名字的枚举。 从这里可以看到,实现 TBase的类具有可序列化的功能。
TStruct
接下来是 本 Struct 的一个描述, TStruct 只是对 struct 名字的一个描述的封装类,并没有什么特别。
1 2 3 4 5 6 7 8 9 10 11 private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Friends" );public final class TStruct { public final String name; public TStruct () { this ("" ); } public TStruct (String n) { this .name = n; } }
TStruct 类似的文件是 Thrift 协议层的一个定义,另外协议层还有一下方法
协议层
这一层主要是用来定义如何序列化数据,属于协议层的一部分,将会在其他章节详细讨论,本节不在详细讲述。
TProtocol
TBinaryProtocol
TCompactProtocol
TJSONProtocol
TProtocolFactory
TProtocolUtil
TSimpleJSONProtocol
TTupleProtocol
TProtocolException
元数据
这里定义的各种其他元数据的类声明
TBase64Utils : Base64 数据的编解码工具
TField : Field 字段的声明
TList : List 结构的声明
TMap : Map 结构的声明
TMessage : Message 结构的声明
TMessageType : Message类型
TSet : set类型的声明
TStruct : struct 类型的声明
TType : thrift 所有支持类型的枚举
TType
Thrift 所有支持的类型 都在 TType 里定义的,我们看一下TType的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public final class TType { public static final byte STOP = 0 ; public static final byte VOID = 1 ; public static final byte BOOL = 2 ; public static final byte BYTE = 3 ; public static final byte DOUBLE = 4 ; public static final byte I16 = 6 ; public static final byte I32 = 8 ; public static final byte I64 = 10 ; public static final byte STRING = 11 ; public static final byte STRUCT = 12 ; public static final byte MAP = 13 ; public static final byte SET = 14 ; public static final byte LIST = 15 ; public static final byte ENUM = 16 ; }
TMessage
这个是 封装结构元数据的上层类, 当我们调用RPC 的时候, Thrift 会将我们要求我们按照他的格式封装元数据,然后在通过TMessage 封装元数据,然后通过网络发送出去。
1 2 3 4 5 6 public final class TMessageType { public static final byte CALL = 1 ; public static final byte REPLY = 2 ; public static final byte EXCEPTION = 3 ; public static final byte ONEWAY = 4 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public final class TMessage { public final String name; public final byte type; public final int seqid; public TMessage () { this ("" , (byte )0 , 0 ); } public TMessage (String n, byte t, int s) { this .name = n; this .type = t; this .seqid = s; } public String toString () { return "<TMessage name:'" + this .name + "' type: " + this .type + " seqid:" + this .seqid + ">" ; } public boolean equals (Object other) { return other instanceof TMessage ? this .equals((TMessage)other) : false ; } public boolean equals (TMessage other) { return this .name.equals(other.name) && this .type == other.type && this .seqid == other.seqid; } }
TMap 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class TMap { public final byte keyType; public final byte valueType; public final int size; public TMap () { this ((byte )0 , (byte )0 , 0 ); } public TMap (byte k, byte v, int s) { this .keyType = k; this .valueType = v; this .size = s; } }
TSet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public final class TSet { public final byte elemType; public final int size; public TSet () { this ((byte )0 , 0 ); } public TSet (byte t, int s) { this .elemType = t; this .size = s; } public TSet (TList list) { this (list.elemType, list.size); } }
TList
1 2 3 4 5 6 7 8 9 10 11 12 13 public final class TList { public TList () { this (TType.STOP, 0 ); } public TList (byte t, int s) { elemType = t; size = s; } public final byte elemType; public final int size; }
TField
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class TField { public final String name; public final byte type; public final short id; public TField () { this ("" , TType.STOP, (short )0 ); } public TField (String n, byte t, short i) { name = n; type = t; id = i; } public String toString () { return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">" ; } public boolean equals (TField otherField) { return type == otherField.type && id == otherField.id; } }
Friends 中每个变量都会用 TField 来封装,如:
1 2 private static final TField NO_FIELD_DESC = new TField("No" , TType.I16, (short )1 );
里面记录了 Field 的名字,类型,和FieldId , thrift 会给每一个Struct的成员变量增加一个Field封装类。
IScheme
IScheme 的作用
除了变量的定义之外 Friends 类当中还有一个概念 叫 Scheme
1 2 3 4 5 private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); static { schemes.put(StandardScheme.class , new FriendsStandardSchemeFactory ()) ; schemes.put(TupleScheme.class , new FriendsTupleSchemeFactory ()) ; }
看一下继承关系
FriendsStandardSchemeFactory 就是 Scheme 的工厂,意思就是新建一个 FriendsStandardScheme。 而 FriendsStandardScheme 又去实现了 IScheme 这个接口。 从而实现了 read 和 write 方法。
这个 Scheme 是干什么呢, 和 FriendsStandardScheme, Friends 又是什么关系呢?
我们知道,Friends 是继承 TBase 的 TBase 中有两个重要的方法
1 2 void read (TProtocol var1) throws TException ;void write (TProtocol var1) throws TException ;
而 Friends 就是要实现这两个方法,
1 2 3 4 5 6 7 public void read (org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { schemes.get(iprot.getScheme()).getScheme().read(iprot, this ); } public void write (org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { schemes.get(oprot.getScheme()).getScheme().write(oprot, this ); }
Scheme 其实就是一种序列化方式,标识该类的具体读写策略、
Friends 序列化的时候,需要传递协议,也就是 TProtocol 的实现类,TProtocol 会根据自身支持的 序列化方式选择来选择 Friends 所支持的序列化方式,也就是的对应的具体 Scheme工厂。从而创建出具体的 Scheme 实现类。
上述代码中 schemes.get(oprot.getScheme()).getScheme(),第一个oprot.getScheme() 是选择对应的Scheme 方式,也就是 StandardScheme 还是 TupleScheme。 从 Friends 静态对象 schemes 中找到Scheme 工厂然后实例化具体的 Scheme 的实现类。
本例中,Friends 一共实现类两种实现类 一种是 FriendsStandardScheme, 一种是·FriendsTupleScheme
来看代码
1 2 3 4 5 6 7 8 9 10 11 private static class FriendsTupleSchemeFactory implements SchemeFactory { public FriendsTupleScheme getScheme () { return new FriendsTupleScheme(); } } private static class FriendsStandardSchemeFactory implements SchemeFactory { public FriendsStandardScheme getScheme () { return new FriendsStandardScheme(); } }
IScheme 具体实现方式。
下面我们具体来分析一下 FriendsStandardScheme 和 FriendsTupleSchemeFactory 具体的读写策略。
FriendsStandardScheme
我们来具体分析一下 FriendsStandardScheme
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private static class FriendsStandardScheme extends StandardScheme <Friends > { public void read (org.apache.thrift.protocol.TProtocol iprot, Friends 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 : if (schemeField.type == org.apache.thrift.protocol.TType.I16) { struct.No = iprot.readI16(); struct.setNoIsSet(true ); } else { org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break ; default : org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } iprot.readFieldEnd(); } iprot.readStructEnd(); if (!struct.isSetNo()) { throw new org.apache.thrift.protocol.TProtocolException("Required field 'No' was not found in serialized data! Struct: " + toString()); } struct.validate(); } public void write (org.apache.thrift.protocol.TProtocol oprot, Friends struct) throws org.apache.thrift.TException { struct.validate(); oprot.writeStructBegin(STRUCT_DESC); oprot.writeFieldBegin(NO_FIELD_DESC); oprot.writeI16(struct.No); oprot.writeFieldEnd(); oprot.writeFieldStop(); oprot.writeStructEnd(); } }
Write 操作
从代码来分析 write 方法是写入规则,首先写入了 STRUCT_DESC 描述, 然后写入了 NO_FIELD_DESC 就是 No 这个字段,然后根据No 的大小吸入了No的具体的值,因为 No 是i16的类型,所以调用了 writeI16 的方法,然后 writeFieldEnd 标识该字段结束。
读操作
读取操作其实很写入操作相反, 需要注意一点是 读操作有个标记为 为 schemeField.type == org.apache.thrift.protocol.TType.STOP
遇到这个标记为,则读取直接停止。那么这个 STOP 是什么作用呢?
STOP 是 writeFieldStop 结束的标记为,标记该 Struct 的所有 Field 都已经读取完毕
从图中我们可以看到, 当Field 写入完毕后 wirte 会写入 TType.STOP 标记,代表所有的Field 都已经图区完毕,这个时候 read 就可以退出了。
而在每个Field,Struct 协议的最后,又会写入结束的标记。
这个标记是由协议来实现的,目前不同的协议有不同的写入方法
例如
1 2 3 4 5 6 7 public void writeFieldEnd () {}public void writeFieldEnd () throws TException { this .writeJSONObjectEnd(); }
这里有个疑问,为什么 TBinaryProtocol 什么也不写呢?
问知道 TField 中有个 type 的字段, type 字段标明了 value 类型所占的长度,
所有我们用 往后读 type 所占的长度就可以获取 value 的值,所以 Field 的结束标记位可以什么都不用写。
那么又有读者会问,那么 String 类型呢,他没有固定长度。
二级制协议 TBinaryProtocol 中关于 写 String 写入的代码如下:
1 2 3 4 5 6 7 8 9 public void writeString (String str) throws TException { try { byte [] dat = str.getBytes("UTF-8" ); this .writeI32(dat.length); this .trans_.write(dat, 0 , dat.length); } catch (UnsupportedEncodingException var3) { throw new TException("JVM DOES NOT SUPPORT UTF-8" ); } }
从这里我们可以看出, 写 String string 转成UTF8的编码,然后在value的地方前两个字节写入字符串的长度,然后在写入字符创的 byte 数据。
所以说,对于变长类型,写入的规则为 长度 + 数据 的方式。
FriendsTupleScheme
FriendsTupleScheme 重新定义了一种名为 TTupleProtocol 的协议,这个协议是一个
TCompactProtocol 的子类, TCompactProtocol 我会在协议层分析。
简单说 FriendsTupleScheme 一种压缩的编码方式,将所有的指以固定长度压缩在一起。结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private static class FriendsTupleScheme extends TupleScheme <Friends > { @Override public void write (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol oprot = (TTupleProtocol) prot; oprot.writeI16(struct.No); } @Override public void read (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol iprot = (TTupleProtocol) prot; struct.No = iprot.readI16(); struct.setNoIsSet(true ); } }
GET SET 方法
SET 方法
Friends 为每一个Field 都生成了一个 set 方法 和一个 isSetXXX 的方法,标识该值是否被set。之所以有这个方式是因为 当我们在给 一个 i31的数 定义为 optional 的时候,i32 对应的是Java 语言的int 型 那么 这个值被初始化为 0,那么当rpc 过来的 这个值 就是0的时候,程序无法区分 这个0 是没有set 还是值就是0
注意,在设计RPC 协议或者其他协议的时候,避免 0 这个值。
Thrift 会给String 每个字段顶一个id, 名字为:__{filed name}_ISSET_ID,又会定义个bitSet 结构 bitSet 的大小为字段的个数。
当我们执行 setXX 这个方法的时候会调用 setXXIsSet(true) 方法。 来标明这个方法已经赋过值了。
setXXIsSet(true) 本意就是讲 这个字段对应的BitSet 的位置控制成1。
1 2 3 4 5 6 7 8 private static final int __NO_ISSET_ID = 0 ;private BitSet __isset_bit_vector = new BitSet(1 );public Friends (short No) { this (); this .No = No; setNoIsSet(true ); }
同样我们还可以判断这个指是否
1 2 3 public boolean isSetNo () { return __isset_bit_vector.get(__NO_ISSET_ID); }
FieldMetaData 结构
Thrift 对于每个生成的类会有一个 FieldMetaData 的结构
这个类会和 Field 一个名称的枚举类型来对应成一个map结构。
FieldMetaData 主要是封装了 Field 值的一些元数据。
1 2 3 4 5 6 7 8 public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class ) ; tmpMap.put(_Fields.NO, new org.apache.thrift.meta_data.FieldMetaData("No" , org.apache.thrift.TFieldRequirementType.REQUIRED, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I16))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Friends.class , metaDataMap ) ; }
首先我们来看一下meta包下都有哪些字段
EnumMetaData
FieldMetaData
FieldValueMetaData
ListMetaData
MapMetaData
SetMetaData
StructMetaData
继承关系类图
FieldMetaData
这个数据结构是Friends 用来说明其中涉及Field信息的元数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class FieldMetaData implements java .io .Serializable { public final String fieldName; public final byte requirementType; public final FieldValueMetaData valueMetaData; private static Map<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>> structMap; static { structMap = new HashMap<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>>(); } public FieldMetaData (String name, byte req, FieldValueMetaData vMetaData) { this .fieldName = name; this .requirementType = req; this .valueMetaData = vMetaData; } }
Friends 或 new 一个这个出来,并传入了相应的信息,我们struct的中的required optional 的关键字 就是通过 这个常量类来定义的。
1 2 3 4 5 6 7 8 public final class TFieldRequirementType { public static final byte REQUIRED = 1 ; public static final byte OPTIONAL = 2 ; public static final byte DEFAULT = 3 ; public TFieldRequirementType () { } }
FieldValueMetaData
既然大部分是继承 FieldValueMetaData 那么我们首先来看这个数据结构,
1 2 3 4 5 6 7 public class FieldValueMetaData implements java .io .Serializable { public final byte type; private final boolean isTypedefType; private final String typedefName; private final boolean isBinary; }
获取FiledMetaData
全局获取
当我们新建好了 FiledMetaData之后可以通过 FiledMetaData 静态类来获取,
FiledMetaData.getStructMetaDataMap(Friends.class)
1 2 Friends friends = new Friends(); friends.getFieldValue(Friends._Fields.NO);
复杂符合类型
我们的 Friends 这是简单定义了 Friends 一个i16的字段No
如果我们在Friends 里增加一些复杂类型,Java 文件又是什么情况呢?
这里还要还有一个问题,我们只是分析了Struct 的生成类,那么Service的生成类是怎样的呢?
由于篇幅问题,我们这个话题留在下期去讨论。
总结
本节主要是总结了通过struct 文件生成的代码,主要谈论了框架生成的一些特性。更有一些协议层的相关的方法,thrift 的Java 代码非常轻量级,分层也非常明确,那么这些东西将会如何组织,整个的分层又是什么呢?我们下一节在详细论述。