22 July 2014
用Go语言开发Thrift接口的hello, world。

三个月没在公司,回来后发现公司内部已经用上了facebook开源的Apache Thrift。大概看了一下介绍,传统接口编写是使用json或者xml作为信息格式进行传输。一般Web Service里面,SOAP这种,使用的就是xml(不过我从来没用过。。);而轻量级网络服务REST,则用的是json作为传输媒介。json相较于xml,传输的内容变少了许多,传输更加便捷。这两种都是基于HTTP的传输方式。

而Apache Thrift,是更加轻量级的Web Service。本科做网络游戏的时候也接触过,叫RPC(Remote Procudure Call,远程过程调用)。这应该也算是一种RPC吧。调用起来更加简单,根本不需要发送HTTP请求,解析返回结果这些操作。只需要预先定义好类和对应函数,调用的时候创建一个类,调用函数即可。返回内容也是直接映射到类当中的。

完整地了解了一下Apache Thrift,这篇文章主要介绍hello, world的编写。如果你之前还没接触过Thrift,可以参考我之前写过的几篇文章所谓参考。

Apache Thrift给了一个完整的例子,包含很多语言版本的,Golang的完整代码可以看这里。文档方面官网没对这个例子介绍多少,代码也没贴全,找到这个就好办多了,直接复制下来编译就行了。其他语言的实现,返回向上一级目录即可。

这篇文章要讲的hello, world 主要根据developWorks的文章Apache Thrift - 可伸缩的跨语言服务开发框架进行介绍。

Thrfit文件定义如下:Hello.thrift。接口Hello,包含五个函数,五个都必须实现,否则编译器会认为该接口并没有被实现。

namespace java service.demo 
service Hello{ 
	string helloString(1:string para) 
	i32 helloInt(1:i32 para) 
	bool helloBoolean(1:bool para) 
	void helloVoid() 
	string helloNull() 
}

.thrfit文件就是定义了一套接口。通过命令可以生成对应语言的接口定义。会在当前目录下生成gen-go文件夹,把里面的内容放到gopath里即可。

$ thrift --gen go Hello.thrift

自动生成的接口定义如下hello.go

type Hello interface {
	// Parameters:
	//  - Para
	HelloString(para string) (r string, err error)
	// Parameters:
	//  - Para
	HelloInt(para int32) (r int32, err error)
	// Parameters:
	//  - Para
	HelloBoolean(para bool) (r bool, err error)
	HelloVoid() (err error)
	HelloNull() (r string, err error)
}

上述操作用来生成接口,有了接口之后,就是要实现接口、开发客户端和开发服务器端这三个部分。参考的例子是使用大Java实现的,果断用Go重写helloimpl.go。Go语言实现接口可以参考Golang 接口实现

type HelloHandler struct {
}

func NewHelloHandler() *HelloHandler {
	return &HelloHandler{}
}

func (h *HelloHandler) HelloString(para string) (string, error) {
	return "hello, world", nil
}

func (h *HelloHandler) HelloBoolean(para bool) (r bool, err error) {
	return para, nil
}

func (h *HelloHandler) HelloInt(para int32) (r int32, err error) {
	return para, nil
}

func (h *HelloHandler) HelloVoid() (err error) {
	return nil
}

func (h *HelloHandler) HelloNull() (r string, err error) {
	return "hello null", nil
}

服务器端的实现helloserver.go

func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string) error {
	var err error
	transport, err = thrift.NewTServerSocket(addr)

	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%T\n", transport)
	handler := NewHelloHandler()
	processor := hello.NewHelloProcessor(handler)
	server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)

	fmt.Println("Starting the simple server... on ", addr)
	return server.Serve()
}

func main() {
	transportFactory := thrift.NewTTransportFactory()
	protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
	transportFactory = thrift.NewTBufferedTransportFactory(8192)
	runServer(transportFactory, protocolFactory, "localhost:9090")
}

启动服务器

$ go run helloserver.go helloimpl.go
Starting the simple server... on localhost:9090

客户端代码helloclient.go

func handleClient(client *hello.HelloClient) error {
	str, err := client.HelloString("")
	fmt.Println(str)
	fmt.Println("----------------")
	return err
}

func runClient(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string) error {
	var transport thrift.TTransport
	var err error

	transport, err = thrift.NewTSocket(addr)

	if err != nil {
		fmt.Println("Error opening socket:", err)
		return err
	}
	transport = transportFactory.GetTransport(transport)
	defer transport.Close()
	if err := transport.Open(); err != nil {
		return err
	}
	fmt.Println(transport, protocolFactory)
	return handleClient(hello.NewHelloClientFactory(transport, protocolFactory))
}

func main() {
	transportFactory := thrift.NewTTransportFactory()
	protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
	transportFactory = thrift.NewTBufferedTransportFactory(8192)
	runClient(transportFactory, protocolFactory, "localhost:9090")
}

启动客户端及运行结果:

$ go run helloclient.go
hello, world

参考文献

原文链接:Golang开发Thrift接口,转载请注明来源!

EOF