什么是Thrift
thrift最初由facebook开发用做系统内各语言之间的RPC通信 。2007年由facebook贡献到apache基金 ,08年5月进入apache孵化器 。支持多种语言之间的RPC方式的通信:php语言client可以构造一个对象,调用相应的服务方法来调用java语言的服务 ,跨越语言的C/S RPC调用 。
thrift的相关下载地址
http://thrift.apache.org/download
https://github.com/apache/thrift
thrift包含了4块功能
- 网络传输和多种通信方式的实现
- 多种网络编码的支持和定制
- RPC框架实现(序列化/反序列化)
- 多种语言的导出
如果通过thrift搭建一个C#的小程序,参考这篇
https://www.cnblogs.com/xxxteam/articles/3277961.html
RPC应用在开发中的价值
今天,主要一起来看下Thrift RPC框架的生成和工作方式。
为什么我们需要RPC?
首要原因就是减少程序员的工作量,降低协议不同步的可能。在一个大型的项目中往往有较多的程序员一起协作。而网络协议的修改时,由于各种原因,很容易造成C/S协议不同步,造成一些运行时的BUG。RPC的好处在于,可以将这些运行时的问题,通过RPC框架的帮助转化成编译时的错误。让机器帮我们去检查,这个想法确实够棒。
其次,序列化代码从手写变成自动化生成后,对整个过程做扩展重构更加方便。比如换一种序列化方式(二进制换成Json),支持下可空类型的传输。
第三,因为RPC协议是自动化导出的,所以很容易实现切换语言,完成通信。
第四,序列化和反序列化代码自动化生成后,项目的代码架构会更清洁,杜绝了有人随意穿插网络拼包代码污染环境的可能性。
Thrift RPC代码的协作流程
- 定义IDL
- 生成目标语言代码(比如C#)
- 在客户端在需要的位置调用目标代码
- 服务端实现事件回调函数,并注册
下面就简单分析下各个步骤
定义IDL
namespace csharp network.rpc
struct BagItem
{
1: i32 ItemId,
2: i32 ItemCount,
3: string ItemName
}
struct GamePlayer
{
1: i32 PlayerId,
2: string PlayerName,
3: list<BagItem> Items
}
service C2SService
{
void Ping(),
void UseItem( 1: i32 itemId )
}
定义的语言是一种自创的语言。支持自定义类型(用struct),支持基本的数据类型,数值以及List。
IDL的主要作用在于定义Service以及Service之下的RPC方法。然后,因为对类型的自定义扩展需求,支持了Custom的struct。
这里提一下,每一个变量或参数都需要设置一个下标(1,2,3),主要是序列化的时候存储和读取用到的(部分序列化方法是基于key-value的)。
生成目标语言代码
针对IDL中定义的每一个struct以及Service,都会生成一个对应的类型文件(比如xxx.cs)。
其中struct生成的自动化代码比较简单,基本就是所有属性,外加序列化用到的Read和Write方法。
Service的自动化代码稍微复杂一些
主要分成3个部分
1.RPC方法的接口ISync。这是定义的service在导出语言下的转义。
2.Client和Process类,Client是客户端使用发起RPC调用的类;而Process是服务端使用的解包并调用上层注册的回调方法的类。
3.参数列表的序列化类型。这块有点像struct的代码自动化生成。每个方法的参数列表和返回值也会分别生成一个类型(就像一个自定义的struct一样),同时自动生成Read和Write方法用于序列化。
在客户端在需要的位置调用目标代码
客户端就可以通过Client属性,来完成调用
TTransport transport = new TSocket("127.0.0.1", 60000);
TProtocol protocol = new TBinaryProtocol(transport);
var client = new C2SService.Client(protocol);
transport.Open();
client.Ping();
服务端实现事件回调函数,并注册
服务端需要实现一个实现上文说到的ISync接口的Impl类,并完成注册。
TServerTransport serverTransport = new TServerSocket(serverPort, 0, false);
C2SServiceImpl handle = new C2SServiceImpl();
var processor = new C2SService.Processor(handle);
TServer server = new TThreadPoolServer(processor, serverTransport);
总结
框架的使用非常方便,客户端和服务端程序员都可以在不了解底层的自动化代码是如何工作的情况下进行工作。
RPC框架的生成代码充分简单,并且与序列化的代码(所以也支持序列化算法的适配)尽可能隔离。