这题目一看就是老标题党啦。既然Thrift是一个跨语言的RPC框架,所以本文采用Go和Java来实现,场景是Echo Server,实现十分简单,尽量呈现出Thrift使用的基本流程。

本文将基于Thrift,使用GO来实现一个Echo Server,使用Java来实现一个Echo Client。文中有不对的地方还望大家指出。

1、配置Thrift compiler

本文是在Win10环境下配置的,Thrift 版本是v0.13.0。Thrift compiler 用于编译Thrift脚本文件,生成各种语言的源文件,比如 Go,C++, Java, Python等,开发者将基于编译生成的源文件进行开发。

Thrift compiler 下载。

下载完成后,将thrift.exe文件放在任意文件夹就好了。

thrift01.png

可以把当前文件夹添加进path环境变量,方便使用。

path.png

2、编写Thrift文件

使用Thrift 开发RPC服务需要编写Thrift脚本文件,使用的是Thrift接口定义语言 (IDL)。可以通过IDL在文件中定义一些变量,结构体,服务等。具体的语法可以参考官方文档,这里就不展开了。

如下代码块,建立一个echo.thrift文件。

namespace go echo 是命名空间,go表示要应用于Go语言,echo表示编译后的文件会在echo包下。

service表示定义服务,编译后会生成Echo这个接口,接口中有echoHello这个方法,需要服务器端实现。

//echo.thrift

namespace go echo

namespace java echo

service Echo {

string echoHello(1: string name)

}

3、编译生成Go、Java代码

使用调用thrift编译echo.thrift文件。

#编译命令

thrift -r --gen go echo.thrift

thrift -r --gen java echo.thrift

编译后生成的Go源文件目录如下,需要重点关注一下echo.go 文件。

gen-go.png

编译后生成的Java源文件目录如下。

gen-java.png

4、用Go开发服务端

在echo.go 文件中可以发现Echo接口和EchoHello方法,如下图所示。服务器端要实现这个接口。

server.png

在 echoHandler.go 中实现了Echo接口,EchoHello方法如下代码块。

//echoHandler.go

type EchoHandler struct {

}

func (echo *EchoHandler) EchoHello(ctx context.Context, name string) (r string, err error) {

fmt.Println(name, " access")

return "hello," + name, nil

}

func NewEchoHandler() *EchoHandler {

return &EchoHandler{}

}

启动服务端。

//server.go

func main() {

transport, _ := thrift.NewTServerSocket("localhost:9090")

handler := NewEchoHandler()

processor := echo.NewEchoProcessor(handler)

server := thrift.NewTSimpleServer2(processor, transport)

_ = server.Serve()

}

5、用Java开发客户端

客户端的操作比较简单。

新建EchoClient.java,配置transport和protocol,就可以调用服务端的服务了,实现如下:

public class EchoClient {

public static void main(String[] args) throws TException {

TSocket transport = new TSocket("localhost", 9090);

transport.open();

TProtocol protocol = new TBinaryProtocol(transport);

Echo.Client client = new Echo.Client(protocol);

String resp = client.echoHello("fly"); //RPC调用服务端的方法

System.out.println(resp);

transport.close();

}

}

运行该程序,远程调用服务器端的echoHello方法,服务器端会返回处理结果,如下图所示。

结果.png

到这里,就已经完成了一个简单的跨语言RPC调用啦。

6、原理简析

如果让你去实现一个RPC框架你会怎么去实现呢?

在没有接触Thrift之前,我有基于Zookeeper,Netty实现过一个简单的RPC调用,Zookeeper起到服务注册与服务发现的作用,Netty负责网络传输。客户端的调用会带上参数封装在自定义的协议里边,把协议通过FastJson序列化之后,通过Netty传输给服务端,服务端处理完成之后再类似地把结果传回给客户端。

其实,Thrift简单的原理也是这样的一个过程。Thrift 包含四个主要的组件:Transport,Protocol,Processor和Server。

+-------------------------------------------+

| Server |

| (single-threaded, event-driven etc) |

+-------------------------------------------+

| Processor |

| (compiler generated) |

+-------------------------------------------+

| Protocol |

| (JSON, compact etc) |

+-------------------------------------------+

| Transport |

| (raw TCP, HTTP etc) |

+-------------------------------------------+

Transport层用于服务端与客户端之间的通信。例子中用到的TSocket,TServerSocket,是阻塞型 socket。

Protocol 定义了消息的序列化方式。一般来讲,作为RPC框架,考虑到网络传输的效率,使用二进制的传输协议,这也是thrift默认的传输协议。

Processor负责从输入流中获取请求,将结果写入到输出流中。

Server负责将上述所有功能组合在一起。

7、疑问

写到这里,突然产生了一个疑问,既然有Http请求为什么还要有RPC呢,该怎么去选择使用呢?欢迎小伙伴评论喔。

参考