go Thrift初体验(win10+普通网络)

之前写过一篇go gRPC初体验(win10+普通网络),今天写个姊妹篇,说一说thrift。

thrift和gprc呢,使用都很广泛,以我现在的水平还无法评价孰好孰坏,反正实习的时候我看的程序里,grpc和thrift都有出现,所以说都学习一下,是最保险的。

为什么我先写的grpc呢,因为grpc的官方文档有中文版,凡是先挑简单的做嘛,而thrift,我查了查,好像并没有中文文档,网上虽然也有一些文章,但总觉得不够有代表性,还是跟着官方文档走更让人放心,于是乎,我瞅了瞅英文版的文档,发现竟然还可以,以我的英文水平基本可以看懂,这就好办了,跟着官方文档走就欧克了。

进入正文之前,还是先声明一下受众群体,跟grpc那篇一样,直接截图过来。

安装thrift

首先,我们要安装thrift命令,因为后面要用这个命令生成go代码。

很简单,不需要什么编译安装,官方有现成的exe文件,下载地址:http://archive.apache.org/dist/thrift/

go Thrift初体验(win10+普通网络)_第1张图片

页面是上面这样的,可以看到最新版的是0.13.0版本,就它了,点进去。

go Thrift初体验(win10+普通网络)_第2张图片

我们是windows系统,所以直接下载exe文件就行,下载之后,放到任意PATH路径下面,这里我依旧是选择了放在$GOROOT/bin下,注意,拷贝进去之后,把名字改成thrift.exe,就像下面这样。

go Thrift初体验(win10+普通网络)_第3张图片

到这就完事儿了,验证一下,打开CMD,输入:

thrift --version

 看到版本号,像下面这样,就说明thrift安装成功了。

下载thrift源码

其实呢,如果只是go语言用,下载源码里面的go包就可以了,但是我们为了看官方文档,就把整个项目拉了下来。

 git clone https://github.com/apache/thrift.git $GOPATH/github.com/apache/

这里不得不说,这个下载的速度那是真的快,500kBps,不知道是不是apache官方库带宽大的缘故,反正就是贼快。

go Thrift初体验(win10+普通网络)_第4张图片

下下来之后可以看一下,go语言需要的包在这里。

go Thrift初体验(win10+普通网络)_第5张图片

阅读官方文档

项目下下来了,接下来,就可以看官方文档,就是那个README.md了。刚开始看我是忐忑的,生怕它写的太高深,好在,文档不长,而且我很快就找到了我想要的,就在这里,Project Hierarchy,也就是代码层次结构,这块说了在tutorial路径下是快速开始的教程,哈哈,真是要啥来啥。

go Thrift初体验(win10+普通网络)_第6张图片

tutorial路径下有各个语言文件夹,应该就是各个语言的示例程序,还有一个README.md,很显然,这就是新手教程了。

go Thrift初体验(win10+普通网络)_第7张图片

新手教程很短,这是好事儿,毕竟是英文的,这块也就不盼着它多生动形象了。。。

go Thrift初体验(win10+普通网络)_第8张图片

简单翻译一下:

  • 首先,安装thrift编译器和语言包,这个我们已经做好了。
  • 然后,阅读tutorial.thrift,了解一下thrift文件的语法,欧克,一会儿读一下。
  • 再然后,编译生成程序代码,命令也给出了,很简单,我们用的时候把那个cpp换成go就行了。
  • 再再然后,看看生成的代码。
  • 最后,看看上面语言文件夹里的服务器/客户端样例代码,我就说那些文件夹里是样例代码吧,哈哈。

这一套下来,正是我惯用的初体验流程啊,哈哈,啥也不说了,后面就跟着它说的一步一步走了。

熟悉thrift语法

本来初体验呢,是不想学的太深的,但是瞅了眼tutorial.thrift,发现挺简单的,甚至作者还有些小幽默,所以这里还是推荐初学者看一看,我这里也简单说一说几个要点。

注释

前面一大堆都是注释,注释有三种形式:

#
/**/
//

也就是shell的注释语法,和c的注释语法,在thrift文件里都有效。

变量类型

这里给出了thrift支持的变量类型,包括下面这些:

 *  bool        Boolean, one byte
 *  i8 (byte)   Signed 8-bit integer
 *  i16         Signed 16-bit integer
 *  i32         Signed 32-bit integer
 *  i64         Signed 64-bit integer
 *  double      64-bit floating point value
 *  string      String
 *  binary      Blob (byte array)
 *  map  Map from one type to another
 *  list    Ordered list of one type
 *  set     Set of unique elements of one type

也就是说在thrift里写这些类型,编译之后就会生成编程语言里对应类型的变量。

引用其他thrift文件

这个瞅着挺厉害,还能像C一样include,像下面这样:

include "shared.thrift"

这样的话,就可以把其他thrift文件里面定义的结构体或者方法引过来用了。

结构体

结构体,这个是最常用的,这里面我们可以为变量添加optional属性,加了这个属性的变量是可以省略的,或者说不是必须要有的。

struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

还有常量、枚举、异常,不说了,略过。

服务

最后说服务的定义,这块作者为了展示thrift支持继承,特意给了个继承的写法。

service Calculator extends shared.SharedService {

  /**
   * A method definition looks like C code. It has a return type, arguments,
   * and optionally a list of exceptions that it may throw. Note that argument
   * lists and exception lists are specified using the exact same syntax as
   * field lists in struct or exception definitions.
   */

   void ping(),

   i32 add(1:i32 num1, 2:i32 num2),

   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

   /**
    * This method has a oneway modifier. That means the client only makes
    * a request and does not listen for any response at all. Oneway methods
    * must be void.
    */
   oneway void zip()

}

这里面呢,给了几个调用方法,有参数没有参数的,又返回值没返回值的,最后还特别的给了一个oneway熟悉的方法,加了这个属性的调用,只需要服务请求者把请求发出去就行了,不需要回复,有点意思哈。

编译生成程序代码

虽然大致了解了thrift语法了,但是作为初体验,我们还是不自己写了,就用它这个tutorial.thrift吧,毕竟给的样例代码也是基于这个tutorial.thrift生成的代码写的。

首先,我们还是自己在$GOPATH/src下新建一个项目,假装是我们自己在开发一个需要rpc调用的客户端和服务器程序,项目就叫goThrift-test吧,建好之后,把tutorial下面的shared.thrift和tutorial.thrift拷贝到这个项目里。

接下来,就执行教程里给的那个命令,生成go代码。

thrift -r --gen go tutorial.thrift

这个成功以后啥提示都不会有。

但是,我们想要的代码,已经生成了,在一个叫gen-go的文件夹里。

go Thrift初体验(win10+普通网络)_第9张图片

看到生成这么多文件,我是拒绝的,因为不知道都是干啥的啊。。。这点grpc我看就很好,就生成一个go文件。。。

阅读生成的代码

这个生成的代码,如果你用熟了,是不用看的,直接用就完事儿了,但是我是初学者嘛,生成了个啥我都不知道,咋用啊。。。所以,还是按照新手教程说的,看看生成的代码。

问题是,生成那么多个文件,都要看吗?

这个新手教程没说,我只能百度一下,按网上说的大概就是,-remote文件夹里面是客户端示例代码,常量在-const文件里,而最重要的一部分代码,包括结构体,服务,以及序列化反序列化都在和thrift文件同名的那个go文件中,反正就是,这块只看tutorial.go就行了。

修改导入包路径

tutorial.go引用了shared那个thrift文件生成的shared包,这是因为tutorial.thrift文件里include了shared的嘛,还记得吧。但是这会带来一个问题,生成的tutorial.go只知道要引用shared包,却不知道shared包的位置。

go Thrift初体验(win10+普通网络)_第10张图片

可以看到,那个shared是红色的,也就是找不到这个包,因此,我们要加上这个包相对于$GOPATH/src的相对路径。 

"goThrift-test/gen-go/shared"

这样就好了,这样的问题还有几个,如果你现在没有发现,等会儿编译的时候也会报出来,没事儿,到时候都加上路径就没问题了。

序列化/解析组装函数

tutorial.go文件里首先是一堆序列化、反序列化、解析、组装消息内容的函数,这些并不是没用的,比如在写服务器的处理函数的时候,我们可能会用到解析消息内容的函数。

go Thrift初体验(win10+普通网络)_第11张图片

但这些内部具体怎么实现的就不用管了,需要的时候用就行了,这块我也就不细看了。

服务接口

生成的代码里面,我们最感兴趣的还是客户端的调用还有服务端的处理是怎么实现的。客户端还是服务器都要有服务里定义的那几个方法,就是ping、add那些,所以这块我们自然想到了go语言的接口,而thrift生成的代码也确实是定义了一个服务名称命名的接口,就是下面这段。

type Calculator interface {
  shared.SharedService
  //Ahh, now onto the cool part, defining a service. Services just need a name
  //and can optionally inherit from another service using the extends keyword.

  // A method definition looks like C code. It has a return type, arguments,
  // and optionally a list of exceptions that it may throw. Note that argument
  // lists and exception lists are specified using the exact same syntax as
  // field lists in struct or exception definitions.
  Ping(ctx context.Context) (err error)
  // Parameters:
  //  - Num1
  //  - Num2
  Add(ctx context.Context, num1 int32, num2 int32) (r int32, err error)
  // Parameters:
  //  - Logid
  //  - W
  Calculate(ctx context.Context, logid int32, w *Work) (r int32, err error)
  // This method has a oneway modifier. That means the client only makes
  // a request and does not listen for any response at all. Oneway methods
  // must be void.
  Zip(ctx context.Context) (err error)
}

首先,由于是继承的shared,所以里面有shared那个接口,然后,接口里定义了服务中的几个方法。

客户端实现

由于客户端代码没有任何逻辑,只需要把请求参数传递给服务端就可以了,所以客户端的代码thrift直接给我们生成好了。

首先,是一个客户端结构体。

type CalculatorClient struct {
  *shared.SharedServiceClient
}

后面给出了构造它的工厂函数。

客户端结构体实现了上面给的服务接口,也就是实现了接口中的所有方法,比如这个Add方法。

func (p *CalculatorClient) Add(ctx context.Context, num1 int32, num2 int32) (r int32, err error) {
  var _args2 CalculatorAddArgs
  _args2.Num1 = num1
  _args2.Num2 = num2
  var _result3 CalculatorAddResult
  if err = p.Client_().Call(ctx, "add", &_args2, &_result3); err != nil {
    return
  }
  return _result3.GetSuccess(), nil
}

这段其实很好理解,里面那个Call方法肯定就是把参数封装好然后发给服务端。

服务端实现

服务端的话,自然也是要实现上面那个接口,只不过,服务端的处理逻辑不是固定的,所以这块thrift没有办法为我们自动生成,需要我们自己去定义结构体实现这个接口,自己去写每个方法的实现逻辑。

再后面的一些processor啊,还有参数读写之类的方法结构体都是thrift框架用的,就不用管了,阅读代码的部分就先到这里。

查看样例代码

按照新手教程,下一步就是看tutorial文件夹里面给的样例程序了,go语言的是这些。

go Thrift初体验(win10+普通网络)_第12张图片

这里面呢,client.go里封装了客户端的方法,server.go里面封装了服务端的方法,这两个方法都是在main.go里被调用的,也就是说,运行main函数,既可以跑客户端,又可以跑服务器,而跑客户端还是服务器是靠这个选项来区分的。

go Thrift初体验(win10+普通网络)_第13张图片

这块填true,跑起来就是服务端,填false,跑起来就是客户端。

main.go,server.go,client.go这仨都没啥说的,都是调用了thrift框架的方法或者刚刚通过thrift文件生成的代码,要说的是这个handler.go,还记得上面一节看生成的代码的时候,我说过服务端的实现不是生成的,而是要自己写,是没错,这个handler.go里面就是我们自己实现的服务接口,里面有每一个方法的处理逻辑,比如这个Add方法。

func (p *CalculatorHandler) Add(ctx context.Context, num1 int32, num2 int32) (retval17 int32, err error) {
	fmt.Print("add(", num1, ",", num2, ")\n")
	return num1 + num2, nil
}

很简单,先打印调用内容,然后把客户端传过来的两数相加再返回就完事儿了。

欧克,到这儿我们其实已经大致明白thrift框架的用法和原理了,接下来,就跑一下子试试吧,嘿嘿,这才是我最喜欢的。

测试

首先,把服务端跑起来。

go run main.go server.go client.go handler.go

如果报错说找不到包,像下面这样:

或者结构不对,像下面这样:

go Thrift初体验(win10+普通网络)_第14张图片

莫慌,都是包的问题,把所有引用生成包的地方:

import (
	"shared"
	"tutorial"
)

都加上相对$GOPATH/src的路径就好了,像下面这样。

import (
	"goThrift-test/gen-go/shared"
	"goThrift-test/gen-go/tutorial"
)

然后,服务端就跑起来了:

接下来,打开另一个terminal,把main函数里的server那里按上一节说的改成false,再运行,就是客户端了。

go Thrift初体验(win10+普通网络)_第15张图片

可以看到,客户端依次调用了所有的接口,全都得到了我们期待的结果,啦啦啦。

再瞅一眼服务器:

go Thrift初体验(win10+普通网络)_第16张图片

也像我们设计的那样,把调用内容打印了出来,欧克,一切正常,收工!

总结

新手教程的最后一句话是,按着它说的做,足以让你为使用thrift做自己的项目做好准备了,而我想说的是,跟着官方新手教程走,准没错!

这一趟下来,thrift语法也了解了,thrift怎么生成代码也会了,生成的代码怎么用也知道了,哈哈,美滋滋。

当然,实际使用过程中,有很多用法我们还需要通过实战来熟悉,不过有了这次初体验打下的基础,我相信后面遇到再大的问题也不至于毫无头绪了,哈哈。

你可能感兴趣的:(rpc,go)