先简单写一个thrift文件
本地通过thrift编译之后会生成一个java源文件。------编译口令 :thrift -gen java mytestrequest.thrift
编译后的源代码如下:
1 /** 2 * Autogenerated by Thrift Compiler (0.8.0) 3 * 4 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 * @generated 6 */ 7 package com.thrift; 8 9 import org.apache.thrift.scheme.IScheme; 10 import org.apache.thrift.scheme.SchemeFactory; 11 import org.apache.thrift.scheme.StandardScheme; 12 13 import org.apache.thrift.scheme.TupleScheme; 14 import org.apache.thrift.protocol.TTupleProtocol; 15 import java.util.List; 16 import java.util.ArrayList; 17 import java.util.Map; 18 import java.util.HashMap; 19 import java.util.EnumMap; 20 import java.util.Set; 21 import java.util.HashSet; 22 import java.util.EnumSet; 23 import java.util.Collections; 24 import java.util.BitSet; 25 import java.nio.ByteBuffer; 26 import java.util.Arrays; 27 import org.slf4j.Logger; 28 import org.slf4j.LoggerFactory; 29 30 /** 31 * 测试类 32 * 33 */ 34 public class koalasRequest implements org.apache.thrift.TBase<koalasRequest, koalasRequest._Fields>, java.io.Serializable, Cloneable { 35 private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("koalasRequest"); 36 37 private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)1); 38 private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2); 39 private static final org.apache.thrift.protocol.TField ADDRESS_FIELD_DESC = new org.apache.thrift.protocol.TField("address", org.apache.thrift.protocol.TType.STRING, (short)3); 40 41 private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); 42 static { 43 schemes.put(StandardScheme.class, new koalasRequestStandardSchemeFactory()); 44 schemes.put(TupleScheme.class, new koalasRequestTupleSchemeFactory()); 45 } 46 47 public int age; // required 48 public String name; // required 49 public String address; // required 50 51 /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ 52 public enum _Fields implements org.apache.thrift.TFieldIdEnum { 53 AGE((short)1, "age"), 54 NAME((short)2, "name"), 55 ADDRESS((short)3, "address"); 56 57 private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); 58 59 static { 60 for (_Fields field : EnumSet.allOf(_Fields.class)) { 61 byName.put(field.getFieldName(), field); 62 } 63 } 64 65 /** 66 * Find the _Fields constant that matches fieldId, or null if its not found. 67 */ 68 public static _Fields findByThriftId(int fieldId) { 69 switch(fieldId) { 70 case 1: // AGE 71 return AGE; 72 case 2: // NAME 73 return NAME; 74 case 3: // ADDRESS 75 return ADDRESS; 76 default: 77 return null; 78 } 79 } 80 81 /** 82 * Find the _Fields constant that matches fieldId, throwing an exception 83 * if it is not found. 84 */ 85 public static _Fields findByThriftIdOrThrow(int fieldId) { 86 _Fields fields = findByThriftId(fieldId); 87 if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); 88 return fields; 89 } 90 91 /** 92 * Find the _Fields constant that matches name, or null if its not found. 93 */ 94 public static _Fields findByName(String name) { 95 return byName.get(name); 96 } 97 98 private final short _thriftId; 99 private final String _fieldName; 100 101 _Fields(short thriftId, String fieldName) { 102 _thriftId = thriftId; 103 _fieldName = fieldName; 104 } 105 106 public short getThriftFieldId() { 107 return _thriftId; 108 } 109 110 public String getFieldName() { 111 return _fieldName; 112 } 113 } 114 115 // isset id assignments 116 private static final int __AGE_ISSET_ID = 0; 117 private BitSet __isset_bit_vector = new BitSet(1); 118 public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; 119 static { 120 Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); 121 tmpMap.put(_Fields.AGE, new org.apache.thrift.meta_data.FieldMetaData("age", org.apache.thrift.TFieldRequirementType.DEFAULT, 122 new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); 123 tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT, 124 new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); 125 tmpMap.put(_Fields.ADDRESS, new org.apache.thrift.meta_data.FieldMetaData("address", org.apache.thrift.TFieldRequirementType.DEFAULT, 126 new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); 127 metaDataMap = Collections.unmodifiableMap(tmpMap); 128 org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(koalasRequest.class, metaDataMap); 129 } 130 131 public koalasRequest() { 132 } 133 134 public koalasRequest( 135 int age, 136 String name, 137 String address) 138 { 139 this(); 140 this.age = age; 141 setAgeIsSet(true); 142 this.name = name; 143 this.address = address; 144 } 145 146 /** 147 * Performs a deep copy on <i>other</i>. 148 */ 149 public koalasRequest(koalasRequest other) { 150 __isset_bit_vector.clear(); 151 __isset_bit_vector.or(other.__isset_bit_vector); 152 this.age = other.age; 153 if (other.isSetName()) { 154 this.name = other.name; 155 } 156 if (other.isSetAddress()) { 157 this.address = other.address; 158 } 159 } 160 161 public koalasRequest deepCopy() { 162 return new koalasRequest(this); 163 } 164 165 @Override 166 public void clear() { 167 setAgeIsSet(false); 168 this.age = 0; 169 this.name = null; 170 this.address = null; 171 } 172 173 public int getAge() { 174 return this.age; 175 } 176 177 public koalasRequest setAge(int age) { 178 this.age = age; 179 setAgeIsSet(true); 180 return this; 181 } 182 183 public void unsetAge() { 184 __isset_bit_vector.clear(__AGE_ISSET_ID); 185 } 186 187 /** Returns true if field age is set (has been assigned a value) and false otherwise */ 188 public boolean isSetAge() { 189 return __isset_bit_vector.get(__AGE_ISSET_ID); 190 } 191 192 public void setAgeIsSet(boolean value) { 193 __isset_bit_vector.set(__AGE_ISSET_ID, value); 194 } 195 196 public String getName() { 197 return this.name; 198 } 199 200 public koalasRequest setName(String name) { 201 this.name = name; 202 return this; 203 } 204 205 public void unsetName() { 206 this.name = null; 207 } 208 209 /** Returns true if field name is set (has been assigned a value) and false otherwise */ 210 public boolean isSetName() { 211 return this.name != null; 212 } 213 214 public void setNameIsSet(boolean value) { 215 if (!value) { 216 this.name = null; 217 } 218 } 219 220 public String getAddress() { 221 return this.address; 222 } 223 224 public koalasRequest setAddress(String address) { 225 this.address = address; 226 return this; 227 } 228 229 public void unsetAddress() { 230 this.address = null; 231 } 232 233 /** Returns true if field address is set (has been assigned a value) and false otherwise */ 234 public boolean isSetAddress() { 235 return this.address != null; 236 } 237 238 public void setAddressIsSet(boolean value) { 239 if (!value) { 240 this.address = null; 241 } 242 } 243 244 public void setFieldValue(_Fields field, Object value) { 245 switch (field) { 246 case AGE: 247 if (value == null) { 248 unsetAge(); 249 } else { 250 setAge((Integer)value); 251 } 252 break; 253 254 case NAME: 255 if (value == null) { 256 unsetName(); 257 } else { 258 setName((String)value); 259 } 260 break; 261 262 case ADDRESS: 263 if (value == null) { 264 unsetAddress(); 265 } else { 266 setAddress((String)value); 267 } 268 break; 269 270 } 271 } 272 273 public Object getFieldValue(_Fields field) { 274 switch (field) { 275 case AGE: 276 return Integer.valueOf(getAge()); 277 278 case NAME: 279 return getName(); 280 281 case ADDRESS: 282 return getAddress(); 283 284 } 285 throw new IllegalStateException(); 286 } 287 288 /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ 289 public boolean isSet(_Fields field) { 290 if (field == null) { 291 throw new IllegalArgumentException(); 292 } 293 294 switch (field) { 295 case AGE: 296 return isSetAge(); 297 case NAME: 298 return isSetName(); 299 case ADDRESS: 300 return isSetAddress(); 301 } 302 throw new IllegalStateException(); 303 } 304 305 @Override 306 public boolean equals(Object that) { 307 if (that == null) 308 return false; 309 if (that instanceof koalasRequest) 310 return this.equals((koalasRequest)that); 311 return false; 312 } 313 314 public boolean equals(koalasRequest that) { 315 if (that == null) 316 return false; 317 318 boolean this_present_age = true; 319 boolean that_present_age = true; 320 if (this_present_age || that_present_age) { 321 if (!(this_present_age && that_present_age)) 322 return false; 323 if (this.age != that.age) 324 return false; 325 } 326 327 boolean this_present_name = true && this.isSetName(); 328 boolean that_present_name = true && that.isSetName(); 329 if (this_present_name || that_present_name) { 330 if (!(this_present_name && that_present_name)) 331 return false; 332 if (!this.name.equals(that.name)) 333 return false; 334 } 335 336 boolean this_present_address = true && this.isSetAddress(); 337 boolean that_present_address = true && that.isSetAddress(); 338 if (this_present_address || that_present_address) { 339 if (!(this_present_address && that_present_address)) 340 return false; 341 if (!this.address.equals(that.address)) 342 return false; 343 } 344 345 return true; 346 } 347 348 @Override 349 public int hashCode() { 350 return 0; 351 } 352 353 public int compareTo(koalasRequest other) { 354 if (!getClass().equals(other.getClass())) { 355 return getClass().getName().compareTo(other.getClass().getName()); 356 } 357 358 int lastComparison = 0; 359 koalasRequest typedOther = (koalasRequest)other; 360 361 lastComparison = Boolean.valueOf(isSetAge()).compareTo(typedOther.isSetAge()); 362 if (lastComparison != 0) { 363 return lastComparison; 364 } 365 if (isSetAge()) { 366 lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.age, typedOther.age); 367 if (lastComparison != 0) { 368 return lastComparison; 369 } 370 } 371 lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName()); 372 if (lastComparison != 0) { 373 return lastComparison; 374 } 375 if (isSetName()) { 376 lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, typedOther.name); 377 if (lastComparison != 0) { 378 return lastComparison; 379 } 380 } 381 lastComparison = Boolean.valueOf(isSetAddress()).compareTo(typedOther.isSetAddress()); 382 if (lastComparison != 0) { 383 return lastComparison; 384 } 385 if (isSetAddress()) { 386 lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.address, typedOther.address); 387 if (lastComparison != 0) { 388 return lastComparison; 389 } 390 } 391 return 0; 392 } 393 394 public _Fields fieldForId(int fieldId) { 395 return _Fields.findByThriftId(fieldId); 396 } 397 398 public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { 399 schemes.get(iprot.getScheme()).getScheme().read(iprot, this); 400 } 401 402 public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { 403 schemes.get(oprot.getScheme()).getScheme().write(oprot, this); 404 } 405 406 @Override 407 public String toString() { 408 StringBuilder sb = new StringBuilder("koalasRequest("); 409 boolean first = true; 410 411 sb.append("age:"); 412 sb.append(this.age); 413 first = false; 414 if (!first) sb.append(", "); 415 sb.append("name:"); 416 if (this.name == null) { 417 sb.append("null"); 418 } else { 419 sb.append(this.name); 420 } 421 first = false; 422 if (!first) sb.append(", "); 423 sb.append("address:"); 424 if (this.address == null) { 425 sb.append("null"); 426 } else { 427 sb.append(this.address); 428 } 429 first = false; 430 sb.append(")"); 431 return sb.toString(); 432 } 433 434 public void validate() throws org.apache.thrift.TException { 435 // check for required fields 436 } 437 438 private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { 439 try { 440 write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); 441 } catch (org.apache.thrift.TException te) { 442 throw new java.io.IOException(te); 443 } 444 } 445 446 private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { 447 try { 448 // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. 449 __isset_bit_vector = new BitSet(1); 450 read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); 451 } catch (org.apache.thrift.TException te) { 452 throw new java.io.IOException(te); 453 } 454 } 455 456 private static class koalasRequestStandardSchemeFactory implements SchemeFactory { 457 public koalasRequestStandardScheme getScheme() { 458 return new koalasRequestStandardScheme(); 459 } 460 } 461 462 private static class koalasRequestStandardScheme extends StandardScheme<koalasRequest> { 463 464 public void read(org.apache.thrift.protocol.TProtocol iprot, koalasRequest struct) throws org.apache.thrift.TException { 465 org.apache.thrift.protocol.TField schemeField; 466 iprot.readStructBegin(); 467 while (true) 468 { 469 schemeField = iprot.readFieldBegin(); 470 if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 471 break; 472 } 473 switch (schemeField.id) { 474 case 1: // AGE 475 if (schemeField.type == org.apache.thrift.protocol.TType.I32) { 476 struct.age = iprot.readI32(); 477 struct.setAgeIsSet(true); 478 } else { 479 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 480 } 481 break; 482 case 2: // NAME 483 if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { 484 struct.name = iprot.readString(); 485 struct.setNameIsSet(true); 486 } else { 487 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 488 } 489 break; 490 case 3: // ADDRESS 491 if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { 492 struct.address = iprot.readString(); 493 struct.setAddressIsSet(true); 494 } else { 495 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 496 } 497 break; 498 default: 499 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 500 } 501 iprot.readFieldEnd(); 502 } 503 iprot.readStructEnd(); 504 505 // check for required fields of primitive type, which can't be checked in the validate method 506 struct.validate(); 507 } 508 509 public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException { 510 struct.validate(); 511 512 oprot.writeStructBegin(STRUCT_DESC); 513 oprot.writeFieldBegin(AGE_FIELD_DESC); 514 oprot.writeI32(struct.age); 515 oprot.writeFieldEnd(); 516 if (struct.name != null) { 517 oprot.writeFieldBegin(NAME_FIELD_DESC); 518 oprot.writeString(struct.name); 519 oprot.writeFieldEnd(); 520 } 521 if (struct.address != null) { 522 oprot.writeFieldBegin(ADDRESS_FIELD_DESC); 523 oprot.writeString(struct.address); 524 oprot.writeFieldEnd(); 525 } 526 oprot.writeFieldStop(); 527 oprot.writeStructEnd(); 528 } 529 530 } 531 532 private static class koalasRequestTupleSchemeFactory implements SchemeFactory { 533 public koalasRequestTupleScheme getScheme() { 534 return new koalasRequestTupleScheme(); 535 } 536 } 537 538 private static class koalasRequestTupleScheme extends TupleScheme<koalasRequest> { 539 540 @Override 541 public void write(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException { 542 TTupleProtocol oprot = (TTupleProtocol) prot; 543 BitSet optionals = new BitSet(); 544 if (struct.isSetAge()) { 545 optionals.set(0); 546 } 547 if (struct.isSetName()) { 548 optionals.set(1); 549 } 550 if (struct.isSetAddress()) { 551 optionals.set(2); 552 } 553 oprot.writeBitSet(optionals, 3); 554 if (struct.isSetAge()) { 555 oprot.writeI32(struct.age); 556 } 557 if (struct.isSetName()) { 558 oprot.writeString(struct.name); 559 } 560 if (struct.isSetAddress()) { 561 oprot.writeString(struct.address); 562 } 563 } 564 565 @Override 566 public void read(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException { 567 TTupleProtocol iprot = (TTupleProtocol) prot; 568 BitSet incoming = iprot.readBitSet(3); 569 if (incoming.get(0)) { 570 struct.age = iprot.readI32(); 571 struct.setAgeIsSet(true); 572 } 573 if (incoming.get(1)) { 574 struct.name = iprot.readString(); 575 struct.setNameIsSet(true); 576 } 577 if (incoming.get(2)) { 578 struct.address = iprot.readString(); 579 struct.setAddressIsSet(true); 580 } 581 } 582 } 583 584 }
生成了一个500多行的代码,那么我们现在对这个请求对象进行序列化,看看序列化结果。
import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TIOStreamTransport; import java.io.ByteArrayOutputStream; import java.util.Arrays; public class KoalaThriftSerialization { public static void main(String[] args) throws TException { koalasRequest request = new koalasRequest ( ); request.setName ( "小明" ); request.setAge ( 20 ); request.setAddress ( "北京" ); ByteArrayOutputStream out = new ByteArrayOutputStream(); TIOStreamTransport tioStreamTransport = new TIOStreamTransport (out); TProtocol protocol = new TBinaryProtocol (tioStreamTransport); request.write ( protocol ); System.out.println (Arrays.toString ( out.toByteArray () ) ); } }
看一看对于这么个简单的request对象,thrift到底序列化成什么了
实际生产中的传输方式一般采用TFramedTransport,序列化采用二进制序列化协议TBinaryProtocol,当然也有json和压缩协议等等,二进制确实最高效的。
我们来看看thrift的序列化方法。
1 public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { 2 schemes.get(oprot.getScheme()).getScheme().write(oprot, this); 3 }
1 public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException { 2 struct.validate(); 3 4 oprot.writeStructBegin(STRUCT_DESC); 5 oprot.writeFieldBegin(AGE_FIELD_DESC); 6 oprot.writeI32(struct.age); 7 oprot.writeFieldEnd(); 8 if (struct.name != null) { 9 oprot.writeFieldBegin(NAME_FIELD_DESC); 10 oprot.writeString(struct.name); 11 oprot.writeFieldEnd(); 12 } 13 if (struct.address != null) { 14 oprot.writeFieldBegin(ADDRESS_FIELD_DESC); 15 oprot.writeString(struct.address); 16 oprot.writeFieldEnd(); 17 } 18 oprot.writeFieldStop(); 19 oprot.writeStructEnd(); 20 }
1:validate 验证一下request中是否合法,主要是验证在thrift文件里属性为required的,但是实际传输中为空的字段。required字段为空则会报错。
2:writeStructBegin 开始序列化request,TBinaryProtocol什么也不做,当然用户可以自己自定义Protocol。
3: writeFieldBegin 写入age字段开始,下面来看看writeFieldBegin到底写了什么
private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)1);
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.thrift.protocol; 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; public TType() { } }
public void writeFieldBegin(TField field) throws TException { this.writeByte(field.type); this.writeI16(field.id); }
public void writeByte(byte b) throws TException { this.bout[0] = b; this.trans_.write(this.bout, 0, 1); }
public void writeI16(short i16) throws TException { this.i16out[0] = (byte)(255 & i16 >> 8); this.i16out[1] = (byte)(255 & i16); this.trans_.write(this.i16out, 0, 2); }
(1)首先先写入age的类型和序号,age为int类型,int类型在thrift里面设置是byte类型的【8】,在写入age的位置,因为thrift的定义的age的位置序号是1,thrift会写入一下short类型的1,也就说thrift的请求体的成员最大数量不能超过short的最大值32767,一般来说够了,哪有变态的请求体需要几万个成员变量啊,不得把程序员搞死!写入short占用两个字节那么继续写入【0 1】
4: writeI32
public void writeI32(int i32) throws TException { this.i32out[0] = (byte)(255 & i32 >> 24); this.i32out[1] = (byte)(255 & i32 >> 16); this.i32out[2] = (byte)(255 & i32 >> 8); this.i32out[3] = (byte)(255 & i32); this.trans_.write(this.i32out, 0, 4); }
直接将age的值写入,也就是 0 0 0 20 占4个字节
5:writeFieldEnd 写入age字段结束,什么也不做。
以上写入一个int类型的字段就写完了,接下来写入String类型的name和String类型的address同理,需要注意的是
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的时候需要先write字符串的长度,int类型占4个字节,因为string类型占用的字节不固定,反序列化时需要知道字符串类型到底是几个字节。
重复这个过程一直到写完所有的字段,这个过程都是由thrift给java语言自动生成的,很方便吧。
所以对于thrift来说,他的序列化就是对各个字段进行写入 1 字段类型 (1 byte)、2字段位置(2 byte)、3字段内容 ,TBinaryProtocol中对java所有的类型的写入都有封装,小伙伴们可以自行下载代码查看!
高级java交流群:825199617
欢迎热爱源码志同道合的朋友加入。
koalas rpc源码地址https://gitee.com/a1234567891/koalas-rpc