JAVA RPC (四) 之thrift序列化普通对象

时间:2022-08-16 09:13:54

先简单写一个thrift文件

JAVA RPC (四) 之thrift序列化普通对象

本地通过thrift编译之后会生成一个java源文件。------编译口令 :thrift -gen java mytestrequest.thrift

编译后的源代码如下:

 /**
* Autogenerated by Thrift Compiler (0.8.0)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
*/
package com.thrift; import org.apache.thrift.scheme.IScheme;
import org.apache.thrift.scheme.SchemeFactory;
import org.apache.thrift.scheme.StandardScheme; import org.apache.thrift.scheme.TupleScheme;
import org.apache.thrift.protocol.TTupleProtocol;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 测试类
*
*/
public class koalasRequest implements org.apache.thrift.TBase<koalasRequest, koalasRequest._Fields>, java.io.Serializable, Cloneable {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("koalasRequest"); 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);
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);
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); private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
static {
schemes.put(StandardScheme.class, new koalasRequestStandardSchemeFactory());
schemes.put(TupleScheme.class, new koalasRequestTupleSchemeFactory());
} public int age; // required
public String name; // required
public String address; // required /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements org.apache.thrift.TFieldIdEnum {
AGE((short)1, "age"),
NAME((short)2, "name"),
ADDRESS((short)3, "address"); private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
} /**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // AGE
return AGE;
case 2: // NAME
return NAME;
case 3: // ADDRESS
return ADDRESS;
default:
return null;
}
} /**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
} /**
* Find the _Fields constant that matches name, or null if its not found.
*/
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;
}
} // isset id assignments
private static final int __AGE_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.AGE, new org.apache.thrift.meta_data.FieldMetaData("age", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32)));
tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
tmpMap.put(_Fields.ADDRESS, new org.apache.thrift.meta_data.FieldMetaData("address", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(koalasRequest.class, metaDataMap);
} public koalasRequest() {
} public koalasRequest(
int age,
String name,
String address)
{
this();
this.age = age;
setAgeIsSet(true);
this.name = name;
this.address = address;
} /**
* Performs a deep copy on <i>other</i>.
*/
public koalasRequest(koalasRequest other) {
__isset_bit_vector.clear();
__isset_bit_vector.or(other.__isset_bit_vector);
this.age = other.age;
if (other.isSetName()) {
this.name = other.name;
}
if (other.isSetAddress()) {
this.address = other.address;
}
} public koalasRequest deepCopy() {
return new koalasRequest(this);
} @Override
public void clear() {
setAgeIsSet(false);
this.age = 0;
this.name = null;
this.address = null;
} public int getAge() {
return this.age;
} public koalasRequest setAge(int age) {
this.age = age;
setAgeIsSet(true);
return this;
} public void unsetAge() {
__isset_bit_vector.clear(__AGE_ISSET_ID);
} /** Returns true if field age is set (has been assigned a value) and false otherwise */
public boolean isSetAge() {
return __isset_bit_vector.get(__AGE_ISSET_ID);
} public void setAgeIsSet(boolean value) {
__isset_bit_vector.set(__AGE_ISSET_ID, value);
} public String getName() {
return this.name;
} public koalasRequest setName(String name) {
this.name = name;
return this;
} public void unsetName() {
this.name = null;
} /** Returns true if field name is set (has been assigned a value) and false otherwise */
public boolean isSetName() {
return this.name != null;
} public void setNameIsSet(boolean value) {
if (!value) {
this.name = null;
}
} public String getAddress() {
return this.address;
} public koalasRequest setAddress(String address) {
this.address = address;
return this;
} public void unsetAddress() {
this.address = null;
} /** Returns true if field address is set (has been assigned a value) and false otherwise */
public boolean isSetAddress() {
return this.address != null;
} public void setAddressIsSet(boolean value) {
if (!value) {
this.address = null;
}
} public void setFieldValue(_Fields field, Object value) {
switch (field) {
case AGE:
if (value == null) {
unsetAge();
} else {
setAge((Integer)value);
}
break; case NAME:
if (value == null) {
unsetName();
} else {
setName((String)value);
}
break; case ADDRESS:
if (value == null) {
unsetAddress();
} else {
setAddress((String)value);
}
break; }
} public Object getFieldValue(_Fields field) {
switch (field) {
case AGE:
return Integer.valueOf(getAge()); case NAME:
return getName(); case ADDRESS:
return getAddress(); }
throw new IllegalStateException();
} /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
} switch (field) {
case AGE:
return isSetAge();
case NAME:
return isSetName();
case ADDRESS:
return isSetAddress();
}
throw new IllegalStateException();
} @Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof koalasRequest)
return this.equals((koalasRequest)that);
return false;
} public boolean equals(koalasRequest that) {
if (that == null)
return false; boolean this_present_age = true;
boolean that_present_age = true;
if (this_present_age || that_present_age) {
if (!(this_present_age && that_present_age))
return false;
if (this.age != that.age)
return false;
} boolean this_present_name = true && this.isSetName();
boolean that_present_name = true && that.isSetName();
if (this_present_name || that_present_name) {
if (!(this_present_name && that_present_name))
return false;
if (!this.name.equals(that.name))
return false;
} boolean this_present_address = true && this.isSetAddress();
boolean that_present_address = true && that.isSetAddress();
if (this_present_address || that_present_address) {
if (!(this_present_address && that_present_address))
return false;
if (!this.address.equals(that.address))
return false;
} return true;
} @Override
public int hashCode() {
return 0;
} public int compareTo(koalasRequest other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
} int lastComparison = 0;
koalasRequest typedOther = (koalasRequest)other; lastComparison = Boolean.valueOf(isSetAge()).compareTo(typedOther.isSetAge());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetAge()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.age, typedOther.age);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetName()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, typedOther.name);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetAddress()).compareTo(typedOther.isSetAddress());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetAddress()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.address, typedOther.address);
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("koalasRequest(");
boolean first = true; sb.append("age:");
sb.append(this.age);
first = false;
if (!first) sb.append(", ");
sb.append("name:");
if (this.name == null) {
sb.append("null");
} else {
sb.append(this.name);
}
first = false;
if (!first) sb.append(", ");
sb.append("address:");
if (this.address == null) {
sb.append("null");
} else {
sb.append(this.address);
}
first = false;
sb.append(")");
return sb.toString();
} public void validate() throws org.apache.thrift.TException {
// check for required fields
} 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 {
// it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
__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 koalasRequestStandardSchemeFactory implements SchemeFactory {
public koalasRequestStandardScheme getScheme() {
return new koalasRequestStandardScheme();
}
} private static class koalasRequestStandardScheme extends StandardScheme<koalasRequest> { public void read(org.apache.thrift.protocol.TProtocol iprot, koalasRequest 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: // AGE
if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
struct.age = iprot.readI32();
struct.setAgeIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // NAME
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 3: // ADDRESS
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.address = iprot.readString();
struct.setAddressIsSet(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(); // check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
} public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException {
struct.validate(); oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(AGE_FIELD_DESC);
oprot.writeI32(struct.age);
oprot.writeFieldEnd();
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeString(struct.name);
oprot.writeFieldEnd();
}
if (struct.address != null) {
oprot.writeFieldBegin(ADDRESS_FIELD_DESC);
oprot.writeString(struct.address);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
} } private static class koalasRequestTupleSchemeFactory implements SchemeFactory {
public koalasRequestTupleScheme getScheme() {
return new koalasRequestTupleScheme();
}
} private static class koalasRequestTupleScheme extends TupleScheme<koalasRequest> { @Override
public void write(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException {
TTupleProtocol oprot = (TTupleProtocol) prot;
BitSet optionals = new BitSet();
if (struct.isSetAge()) {
optionals.set(0);
}
if (struct.isSetName()) {
optionals.set(1);
}
if (struct.isSetAddress()) {
optionals.set(2);
}
oprot.writeBitSet(optionals, 3);
if (struct.isSetAge()) {
oprot.writeI32(struct.age);
}
if (struct.isSetName()) {
oprot.writeString(struct.name);
}
if (struct.isSetAddress()) {
oprot.writeString(struct.address);
}
} @Override
public void read(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException {
TTupleProtocol iprot = (TTupleProtocol) prot;
BitSet incoming = iprot.readBitSet(3);
if (incoming.get(0)) {
struct.age = iprot.readI32();
struct.setAgeIsSet(true);
}
if (incoming.get(1)) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
}
if (incoming.get(2)) {
struct.address = iprot.readString();
struct.setAddressIsSet(true);
}
}
} }

生成了一个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到底序列化成什么了

JAVA RPC (四) 之thrift序列化普通对象

实际生产中的传输方式一般采用TFramedTransport,序列化采用二进制序列化协议TBinaryProtocol,当然也有json和压缩协议等等,二进制确实最高效的。

我们来看看thrift的序列化方法。

  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
}
 public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException {
struct.validate(); oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(AGE_FIELD_DESC);
oprot.writeI32(struct.age);
oprot.writeFieldEnd();
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeString(struct.name);
oprot.writeFieldEnd();
}
if (struct.address != null) {
oprot.writeFieldBegin(ADDRESS_FIELD_DESC);
oprot.writeString(struct.address);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}

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