在企业应用中RPC的使用可以说是十分的广泛,使用该技术可以方便的与各种程序交互而不用考虑其编写使用的语言。
如果你对RPC的概念还不太清楚,可以点击这里。
现今市面上已经有许多应用广泛的RPC框架,比如GRPC,而今天我们要介绍的是同样使用广泛的Apache Thrift。这篇文章将带你安全越过所有坑点,请放心食用。
Thrift简介
Thrift是Facebook的一个开源项目,后来进入Apache进行孵化。Thrift也是支持跨语言的,所以它有自己的一套IDL。目前它支持几乎所有主流的编程语言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, java script, Node.js, Smalltalk, OCaml and Delphi and other languages。Thrift可以支持多种信息格式,除了Thrift私有的二进制编码规则和一种LVQ(类似于TLV消息格式)的消息格式,还有常规的JSON格式。Thrift的网络协议建立在TCP协议基础上,并且支持阻塞式IO模型和多路IO复用模型。我们将在后文详细讲解Apache Thrift的使用。Thrift也是目前最流行的RPC框架之一,从网络上各种性能测试情况开,Thrift的性能都是领先的。Thrift的官网地址为:http://thrift.apache.org/
安装
首先是安装golang的库:
go get git.apache.org/thrift.git/lib/go/thrift/...
为了能编译Thrift IDL,我们还得编译thrift-compiler,主要的坑全在这里。
首先,如果你在用ubuntu,千万别用apt去安装,因为官方源和所有ppa里的compiler版本严重过时了;
其次网上的单独编译compiler教程不要看,基本都过时了,现在thrift的版本已经是1.0.0-dev,如果按那些教程编译出来的工具是无法和go get安装的库一起使用的,而且很大概率你连正常编译都无法进行,如果不使用go get,那么可以按照这里的方法2进行安装。(方法2里编译compiler的步骤请跳过,使用我在下面要讲的方法)
好了下面我们开始安装thrift-compiler,首先是安装依赖:
sudo apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libboost-filesystem-dev libboost-thread-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
接着我们clone最新的thrift仓库,然后编译:
git clone https://github.com/apache/thrift cd thrift ./bootstrap.sh ./configure --without-qt4 --wihout-qt5 make sudo make install
解释一下,--without-qt*是为了加快编译并防止报错,因为qt始终要依赖moc,很容易出问题而且我们一般也用不到,所以舍去。
如果你也不需要其他语言的生成功能,可以用--without-[name]来去除,具体参见./configure --help。
make也可以改成”make -j N“,N是你cpu可用核心数,并行编译加快速度。
安装好后我们运行thrift命令验证一下安装:
thrift -version
# Thrift version 1.0.0-dev
这样Thrift就算安装完成了。
另外,不使用go get而用源码进行安装时,请保证thrift-compiler和你安装的库的版本相同,否则会有数不清的问题出现。当然,使用go get+自行编译的安装方法不会有这些问题。
Thrift的使用
首先我们要编写IDL文件,定义RPC的接口和数据。如果你还不熟悉thrift IDL的语法,可以参考这里,它和c/c++的语法十分相似,上手起来也较容易。
我们来看一个例子,我们定义一个“compute”模块,在其中定义DivMod(计算商和模)和MulRange(计算阶乘)两个服务,并定义一种包含DivMod计算结果的数据类型:
namespace go compute struct Result { 1: i64 div; 2: i64 mod; } service DivMod { Result DoDivMod(1:i64 arg1, 2:i64 arg2); } service MulRange { string BigRange(1:i64 max) }
然后我们用thrift-compiler将其编译成golang代码:
thrift -r --gen go compute.thrift
它会在当前目录下生成一个gen-go目录,在其中有个compute目录,那就是我们生成的模块,copy出来放在$GOPATH里。
接下来我们来实现服务端,现在我们只定义了接口,而没有实现它,建立服务端需要如下几个步骤:
- 实现服务处理接口,在1.0版本中,接口第一个参数需要是context.Context,用以支持取消或对RPC调用设置超时。
- 创建 Processor,也就是把实现的接口注册进RPC服务创建 Transport,Transport用于管理网络IO。它是一个interface,可以是TSocket,TServerSocket以及NewTHttpClient*或TTransportFactory.GetTransport返回的对象。
- 创建 Protocol,也就是传输协议,一般使用二进制协议来传输数据,当然也可以选择json或其他格式。
- 创建server,使用前面的Processor,Transport,Protocol以及ip地址来创建服务端。
- 运行server。
下面我们先实现接口:
// computeThrift 实现service中定义的方法 type divmodThrift struct { } // 每个方法除了定义的返回值之外还要返回一个error,包括定义成void的方法。自定义类型会在名字之后加一条下划线
// 暂时用不到context,所以忽略
func (d *divmodThrift) DoDivMod(_ context.Context, arg1, arg2 int64) (*compute.Result_, error) { divRes := int64(arg1 / arg2) modRes := int64(arg1 % arg2) // 生成的用于生成自定义数据对象的函数 res := compute.NewResult_() res.Div = divRes res.Mod = modRes return res, nil } // 尽量一个struct对应一个service type mulrangeThrift struct { } func (m *mulrangeThrift) BigRange(_ context.Context, max int64) (st