Thrift的关键其实就是在定义好结构后,通过某种序列化方式进行网络传输交互,一般定义结构可以使用IDL的方式来描述各个字段属性等信息,这种方式可以支持跨语言的使用。对于纯Java的应用,也可以通过在类中添加注解的方式来实现,即把IDL中需要定义的信息,通过注解和注解中的信息来进行描述,同样可以达到定义结构的目的
定义好结构之后,就可以通过对应的TProtocol将数据结构进行发送和接收的序列化和反序列化即可,这次我们主要看下TProtocol中的一个实现–TBinaryProtocol
之前我们也看过thrift支持的一些结构类型,如struct、string、map、list等,具体可以看下对应枚举
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; }
|
每次发送消息,都有一个消息类型的标识
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; }
|
而TProtocol提供了对应这些结构的读写接口,我们可以大致看一下写接口,读接口也类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| writeMessageBegin(name, type, seq); writeMessageEnd();
writeStructBegin(name); writeStructEnd();
writeFieldBegin(name, type, id); writeFieldEnd(); writeFieldStop(); writeMapBegin(ktype, vtype, size); writeMapEnd(); writeListBegin(etype, size); writeListEnd(); writeSetBegin(etype, size); writeSetEnd(); writeBool(bool); writeByte(byte); writeI16(i16); writeI32(i32); writeI64(i64); writeDouble(double); writeString(string);
|
通过阅读生成的代码,我们就可以看到具体的使用了,基本流程大致如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
writeMessageBegin(name, type, seq);
writeStructBegin(name);
writeFieldBegin(name, type, id);
writeString(string);
writeFieldEnd();
writeFieldBegin(name, type, id);
writeString(string);
writeFieldEnd();
writeFieldStop();
writeStructEnd(); writeMessageEnd();
|
根据这些接口,掌握了写入流程后,我们可以不使用生成的代码来完成消息的接收和发送,下面我们只使用TBinaryProtocol来实现一个最简单的server服务(对应的接口参考第一篇文章),
1 2 3 4 5 6 7 8 9 10 11 12 13
| // userService.thrift struct User { 1:string name; 2:i32 age; }
struct UserSearchResult { 1:list<User> users; }
service UserService { UserSearchResult searchUsers(1:string name); }
|
实现代码如下(只是为了展示功能)
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
| TServerTransport serverTransport = new TServerSocket(12345); serverTransport.listen(); final TTransport transport = serverTransport.accept();
TProtocol protocol = new TBinaryProtocol(transport);
while (true) { String name = ""; final TMessage tMessage = protocol.readMessageBegin(); if (tMessage.type == TMessageType.CALL) { protocol.readStructBegin(); final TField tField = protocol.readFieldBegin(); if (tField.id == 1) { name = protocol.readString(); } protocol.readFieldEnd(); protocol.readStructEnd(); } protocol.readMessageEnd();
System.out.println("Read param name:" + name);
final TMessage respMessage = new TMessage("searchUsers", TMessageType.REPLY, tMessage.seqid); protocol.writeMessageBegin(respMessage);
protocol.writeStructBegin(new TStruct("success")); protocol.writeFieldBegin(new TField("success", TType.STRUCT, (short) 0));
protocol.writeStructBegin(new TStruct("searchResult")); final TField tField = new TField("users", TType.LIST, (short) 1); protocol.writeFieldBegin(tField); protocol.writeListBegin(new TList(TType.STRUCT, 2)); protocol.writeStructBegin(new TStruct("user")); protocol.writeFieldBegin(new TField("name", TType.STRING, (short) 1)); protocol.writeString("zhangsan1"); protocol.writeFieldEnd(); protocol.writeFieldBegin(new TField("age", TType.I32, (short) 2)); protocol.writeI32(18); protocol.writeFieldEnd(); protocol.writeFieldStop(); protocol.writeStructEnd(); protocol.writeStructBegin(new TStruct("user")); protocol.writeFieldBegin(new TField("name", TType.STRING, (short) 1)); protocol.writeString("lisi1"); protocol.writeFieldEnd(); protocol.writeFieldBegin(new TField("age", TType.I32, (short) 2)); protocol.writeI32(19); protocol.writeFieldEnd(); protocol.writeFieldStop(); protocol.writeStructEnd(); protocol.writeListEnd(); protocol.writeFieldEnd(); protocol.writeFieldStop(); protocol.writeStructEnd();
protocol.writeFieldEnd(); protocol.writeFieldStop(); protocol.writeStructEnd(); protocol.writeMessageEnd(); protocol.getTransport().flush(); }
|
之后启动服务,在使用之前的Client端方法进行调用
1 2 3 4 5 6 7 8 9 10 11
| TTransport transport = new TSocket("localhost", 12345); transport.open();
TProtocol protocol = new TBinaryProtocol(transport); UserService.Client client = new UserService.Client(protocol);
UserSearchResult userRes = client.searchUsers("zhangsan"); System.out.println(userRes);
transport.close();
|
执行后可以发现可以实现之前的功能
1 2 3 4
| // 服务端终端输出 Read param name:zhangsan // 客户端终端输出 UserSearchResult(users:[User(name:zhangsan1, age:18), User(name:lisi1, age:19)])
|
再进行深入的话,我们可以根据 TBinaryProtocol 中的具体发送信息逻辑,直接使用原生的Java Socket进行编程,只不过会更加麻烦一些,原理基本就是如此