golang高性能RPC:Apache Thrift安装使用完全攻略
在企业应用中RPC的使用可以说是十分的广泛,使用该技术可以方便的与各种程序交互而不用考虑其编写使用的语言。 如果你对RPC的概念还不太清楚,可以点击这里。 现今市面上已经有许多应用广泛的RPC框架,比如GRPC,而今天我们要介绍的是同样使用广泛的Apache Thrift。这篇文章将带你安全越过所有坑点,请放心食用。 Thrift简介 Thrift是Facebook的一个开源项目,后来进入Apache进行孵化。Thrift也是支持跨语言的,所以它有自己的一套IDL。目前它支持几乎所有主流的编程语言:C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,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里。 接下来我们来实现服务端,现在我们只定义了接口,而没有实现它,建立服务端需要如下几个步骤:
// computeThrift 实现service中定义的方法 type divmodThrift struct { } // 每个方法除了定义的返回值之外还要返回一个error,包括定义成void的方法。自定义类型会在名字之后加一条下划线 接口实现之后我们就要建立并启动服务器了,首先我们先建立Transport: // 创建服务器 serverTransport,err := thrift.NewTServerSocket(net.JoinHostPort("127.0.0.1","9999")) if err != nil { fmt.Println("Error!",err) os.Exit(1) } 然后创建传输协议: // 创建二进制协议 protocolFactory := thrift.NewTBinaryProtocolFactoryDefault() transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory()) 接着我们把实现的接口注册成Processor: // 创建Processor,用一个端口处理多个服务 divmodProcessor := compute.NewDivModProcessor(new(divmodThrift)) mulrangeProcessor := compute.NewMulRangeProcessor(new(mulrangeThrift)) multiProcessor := thrift.NewTMultiplexedProcessor() // 给每个service起一个名字 multiProcessor.RegisterProcessor("divmod",divmodProcessor) multiProcessor.RegisterProcessor("mulrange",mulrangeProcessor) 这里我们用TMultiplexedProcessor来实现一个端口监听多个服务。 最后就是启动服务器: // 启动服务器 server := thrift.NewTSimpleServer4(multiProcessor,serverTransport,transportFactory,protocolFactory) server.Serve() 启动服务器这里也有坑点,网上有文章说使用NewTSimpleServer2可以直接启用二进制格式的数据传输,这是错的,想用二进制或其他格式传输数据,必须明确生成对应的ProtocolFactory并使用NewTSimpleServer4创建服务器。官方给的例子里就是这样做的,我也是在被反复坑了数次之后才确认了这个问题。 客户端: 要建立客户端,也要按照如下几个步骤:
下面我们来看代码: func main() { // 先建立和服务器的连接的socket,再通过socket建立Transport socket,err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1","9999")) if err != nil { fmt.Println("Error opening socket:",err) os.Exit(1) } transport := thrift.NewTFramedTransport(socket) // 创建二进制协议 protocol := thrift.NewTBinaryProtocolTransport(transport) // 打开Transport,与服务器进行连接 if err := transport.Open(); err != nil { fmt.Fprintln(os.Stderr,"Error opening socket to "+"localhost"+":"+"9999",err) os.Exit(1) } defer transport.Close() // 接口需要context,以便在长操作时用户可以取消RPC调用 ctx := context.Background() // 使用divmod服务 divmodProtocol := thrift.NewTMultiplexedProtocol(protocol,"divmod") // 创建代理客户端,使用TMultiplexedProtocol访问对应的服务 c := thrift.NewTStandardClient(divmodProtocol,divmodProtocol) client := compute.NewDivModClient(c) res,err := client.DoDivMod(ctx,100,3) if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println(res) // 使用mulrange服务 // 步骤与上面的相同 mulProtocol := thrift.NewTMultiplexedProtocol(protocol,"mulrange") c = thrift.NewTStandardClient(mulProtocol,mulProtocol) client2 := compute.NewMulRangeClient(c) num,err := client2.BigRange(ctx,100) if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println(num) } 当然,大部分情况下我们可能都是用单服务,对于单服务来说,0.11版本的接口和1.0并没有变化,这个找网上的例子即可。 go build之后我们分别运行client和server。 下面是输出,计算了100÷3和100%3的结果,还有100的阶乘,客户端调用接口后检查err,没有错误发生就输出结果: 这样一个支持多服务的RPC示例就完成了。 ? 如果有疑问或者建议,欢迎在评论指出。 祝玩得愉快! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |