Thrift算是一种常见的RPC框架,我工作中也常常接触 到。
基于0.5.0版本
简介
Thrift作为一种接口定义语言(IDL)和二进制通信协议被应用在程序设计的服务创建和定义中,我们更常见称呼Thrift为RPC框架。 在使用Thrift中,我认为Thrift有如下特点:
- 支持语言众多。拥有自成一体的IDL,各种语言C++、Java、Python等都有实现。
- 传输性能优秀。RPC性能对比不错,很多大公司,还有组件(Hadoop、Hive等)都使用它。

组件职责和传输
Thrift官网有介绍Thrift Server的网络层和各组件作用,我们跟着顺藤摸瓜:
+-------------------------------------------+
| Server |
| (single-threaded, event-driven etc) |
+-------------------------------------------+
| Processor |
| (compiler generated) |
+-------------------------------------------+
| Protocol |
| (JSON, compact etc) |
+-------------------------------------------+
| Transport |
| (raw TCP, HTTP etc) |
+-------------------------------------------+
上图给出整体流程图,我这里一一说下:
- Transport 网络传输封装,TCP、HTTP之类。最终传输层协议实现有在类中生成socket,也有会抽象一层TSocket去做。
- TFramedTransport
- TNonblockingTransport
- THttpClient
- Protocol 传输序列化协议
- TBinaryProtocol
- TCompactProtocol
- TJSONProtocol
- TProcessor thrift代码生成,传输及序列化流程处理
- TServer 处理网络流程执行,数据处理整个流程
- TSimpleServer
- TThreadPoolServer
- TNonblockingCompleteServer
- Client和TServiceClient thrift生成的代码,client端调用使用
协议分析
Strict Binary协议
调用示例
enum TypeEnum {
UNKNOWN = 0;
ADMIN = 1;
NORMAL = 2;
}
struct Req {
1:i32 id;
2:TypeEnum type;
3:string name;
4:map<string, string> parameters;
5:bool fill_info;
}
struct UserInfo {
1:i32 id;
2:TypeEnum type;
3:string name;
4:map<string, string> parameters;
5:bool status;
}
service SimpleThriftService {
string knock(1:string msg);
UserInfo getUserInfo(1:Req req);
}
TTransport transport = new TSocket("localhost", 9090);
TProtocol protocol = new TBinaryProtocol(transport);
SimpleThriftService.Client client = new SimpleThriftService.Client(protocol);
transport.open();
Req req = new Req(1, TypeEnum.NORMAL, "test", new HashMap<String, String>() {{put("k", "v");}}, false);
UserInfo userInfo = client.getUserInfo(req);
分析字节码
以使用最广的strict Binary协议为例,基于上面示例调用,进行了抓包,结合文档对字节码进行直观分析。
注:wireshark抓包的话自带了一些方便的标注。
这里只标注了client请求协议体,server reply返回的消息格式相同。
//Message
80 01// 10000000 00000001 首位1标识为strict binary protocol, 其余15位标识版本号
00 // 无用字节
01 // message type消息类型 1:CALL
00 00 00 0b // 方法名长度 11
67 65 74 55 73 65 72 49 6e 66 6f // 方法名:getUserInfo
00 00 00 01 // seq id顺序id,client 用这个号去处理消息的失序响应
//Struct
0c // field类型:T_STRUCT
00 00// field id: 0
00 // 类型: T_STOP
0c // field类型:T_STRUCT
00 01 // field id: 1
08 // field类型:I32
00 01 // field id: 1
00 00 00 01 // int32值=1
08 // field类型:I32
00 02 // field id: 2
00 00 00 02 // int32值=2
0b // field类型:BINARY
00 03 // field id: 3
00 00 00 04 // BINARY field会带length,为4
74 65 73 74 //string值为test
0d // field类型:MAP
00 04 //field id: 4
0b //key type: BINARY
0b //value type: BINARY
00 00 00 01 //map的item数,该例子为1
00 00 00 01 //length:1
6b //值为 k
00 00 00 01 //value length:1
76 // 值为 v
02 // field类型:BOOL
00 05 //field id: 5
00 //值为FALSE
00 //T_STOP 标识STRUCT结束
00 //T_STOP 标识整体数据结束
协议格式

1. 协议头
分为这四种消息类型
- Call: 1 client发送的消息类型,指代需要响应的消息。
- Reply: 2 server回复响应的消息。
- Exception: 3 server异常消息。
- Oneway: 4 client发送的单向消息,不需要返回。
2. 协议体

- 一般定长数据:i32、i64、BOOL等,采用TV模式,字段类型(1字节)+ field id(2字节)+ 值
- 变长数据:STRUCT、BINARY、MAP、LIST、SET等,采用TLV模式,字段类型(1字节)+ field id(2字节)+ field length (4字节) + 值
定长数据类型
| 数据类型 | 字段类型(1个字节) | 类型尺寸(单位:字节) |
|---|---|---|
| bool | 2 | 1 |
| byte | 3 | 1 |
| double | 4 | 8 |
| i16 | 6 | 2 |
| i32 | 8 | 4 |
| i64 | 10 | 8 |
变长数据类型
| 数据类型 |
|---|