从零开始基于go-thrift创建一个RPC服务

Thrift 是一种被广泛使用的 rpc 框架,可以比较灵活的定义数据结构和函数输入输出参数,并且可以跨语言调用。为了保证服务接口的统一性和可维护性,我们需要在最开始就制定一系列规范并严格遵守,降低后续维护成本。

Thrift开发流程是:先定义IDL,使用thrift工具生成目标语言接口(interface)代码,然后进行开发。

官网:
github:https://github.com/apache/thrift/

安装Thrift

将Thrift IDL文件编译成目标代码需要安装Thrift二进制工具。

Mac

建议直接使用brew安装,节省时间:

brew install thrift

安装后查看版本:

$ thrift -version Thrift version 0.12.0

也可以下载源码安装,参考:。

源码地址:?path=http://www.likecs.com/thrift/0.12.0/thrift-0.12.0.tar.gz

CentOS

需下载源码安装,参考:。

Debian/Ubuntu

需下载源码安装,先安装依赖:,然后安装thrift:。

Windows

可以直接下载二进制包。地址:?path=http://www.likecs.com/thrift/0.12.0/thrift-0.12.0.exe。

实战

该小节我们通过一个例子,讲述如何使用Thrift快速开发出一个RPC微服务,涉及到Golang服务端、Golang客户端、PHP客户端、PHP服务端。项目名就叫做thrift-sample,代码托管在 https://github.com/52fhy/thrift-sample。

推荐使用Golang服务端实现微服务,PHP客户端实现调用。

编写thrift IDL thrift ├── Service.thrift └── User.thrift

User.thrift

namespace go Sample namespace php Sample struct User { 1:required i32 id; 2:required string name; 3:required string avatar; 4:required string address; 5:required string mobile; } struct UserList { 1:required list<User> userList; 2:required i32 page; 3:required i32 limit; }

Service.thrift

include "User.thrift" namespace go Sample namespace php Sample typedef map<string, string> Data struct Response { 1:required i32 errCode; //错误码 2:required string errMsg; //错误信息 3:required Data data; } //定义服务 service Greeter { Response SayHello( 1:required User.User user ) Response GetUser( 1:required i32 uid ) }

说明:
1、namespace用于标记各语言的命名空间或包名。每个语言都需要单独声明。
2、struct在PHP里相当于class,golang里还是struct。
3、service在PHP里相当于interface,golang里是interface。service里定义的方法必须由服务端实现。
4、typedef和c语言里的用法一致,用于重新定义类型的名称。
5、struct里每个都是由1:required i32 errCode;结构组成,分表代表标识符、是否可选、类型、名称。单个struct里标识符不能重复,required表示该属性不能为空,i32表示int32。

接下来我们生产目标语言的代码:

mkdir -p php go #编译 thrift -r --gen go thrift/Service.thrift thrift -r --gen php:server thrift/Service.thrift

其它语言请参考上述示例编写。

编译成功后,生成的代码文件有:

gen-go └── Sample ├── GoUnusedProtection__.go ├── Service-consts.go ├── Service.go ├── User-consts.go ├── User.go └── greeter-remote └── greeter-remote.go gen-php └── Sample ├── GreeterClient.php ├── GreeterIf.php ├── GreeterProcessor.php ├── Greeter_GetUser_args.php ├── Greeter_GetUser_result.php ├── Greeter_SayHello_args.php ├── Greeter_SayHello_result.php ├── Response.php ├── User.php └── UserList.php

注:如果php编译不加:server则不会生成GreeterProcessor.php文件。如果无需使用PHP服务端,则该文件是不需要的。

golang服务端

本节我们实行golang的服务端,需要实现的接口我们简单实现。本节参考了官方的例子,做了删减,官方的例子代码量有点多,而且是好几个文件,对新手不太友好。建议看完本节再去看官方示例。官方例子:https://github.com/apache/thrift/tree/master/tutorial/go/src。

首先我们初始化go mod:

$ go mod init sample

然后编写服务端代码:
main.go

package main import ( "context" "encoding/json" "flag" "fmt" "github.com/apache/thrift/lib/go/thrift" "os" "sample/gen-go/Sample" ) func Usage() { fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], ":\n") flag.PrintDefaults() fmt.Fprint(os.Stderr, "\n") } //定义服务 type Greeter struct { } //实现IDL里定义的接口 //SayHello func (this *Greeter) SayHello(ctx context.Context, u *Sample.User) (r *Sample.Response, err error) { strJson, _ := json.Marshal(u) return &Sample.Response{ErrCode: 0, ErrMsg: "success", Data: map[string]string{"User": string(strJson)}}, nil } //GetUser func (this *Greeter) GetUser(ctx context.Context, uid int32) (r *Sample.Response, err error) { return &Sample.Response{ErrCode: 1, ErrMsg: "user not exist."}, nil } func main() { //命令行参数 flag.Usage = Usage protocol := flag.String("P", "binary", "Specify the protocol (binary, compact, json, simplejson)") framed := flag.Bool("framed", false, "Use framed transport") buffered := flag.Bool("buffered", false, "Use buffered transport") addr := flag.String("addr", "localhost:9090", "Address to listen to") flag.Parse() //protocol var protocolFactory thrift.TProtocolFactory switch *protocol { case "compact": protocolFactory = thrift.NewTCompactProtocolFactory() case "simplejson": protocolFactory = thrift.NewTSimpleJSONProtocolFactory() case "json": protocolFactory = thrift.NewTJSONProtocolFactory() case "binary", "": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() default: fmt.Fprint(os.Stderr, "Invalid protocol specified", protocol, "\n") Usage() os.Exit(1) } //buffered var transportFactory thrift.TTransportFactory if *buffered { transportFactory = thrift.NewTBufferedTransportFactory(8192) } else { transportFactory = thrift.NewTTransportFactory() } //framed if *framed { transportFactory = thrift.NewTFramedTransportFactory(transportFactory) } //handler handler := &Greeter{} //transport,no secure var err error var transport thrift.TServerTransport transport, err = thrift.NewTServerSocket(*addr) if err != nil { fmt.Println("error running server:", err) } //processor processor := Sample.NewGreeterProcessor(handler) fmt.Println("Starting the simple server... on ", *addr) //start tcp server server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory) err = server.Serve() if err != nil { fmt.Println("error running server:", err) } }

编译并运行:

$ go run main.go Starting the simple server... on localhost:9090 客户端

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzdxy.html