创建RPC服务之Golang和python实践
昨天用Python内置的XMLRPCServer实现了一个简单的RPC服务。今天又用了一下Thirft框架,感觉不错,尤其这种跨语言应用之间的调用很是牛逼!
对于RPC服务,有两个重要的角色:服务端和客户端。根据Thrift的协议来定义server和client还是比较简单的,但前提是你要熟悉Thrift的工作原理。关于Thrift的介绍可以看这篇文章:Thrift架构介绍。
我记录一下我的实践中遇到的一些问题。
1、安装
Thrift在Windows下的安装比较简单,直接在官网下载exe程序即可,在Linux下稍微麻烦写,要安装一些依赖库,还要手动进行make等编译操作,完全按照官网操作即可。不过在安装中也遇到很多问题,比如,JAVA的版本问题,我之前装了一个比较高的版本,但ant依赖的版本是低版本。
第二次补充(2018年1月30日),thrift最新版本是0.11.0,go-thrift对应也是支持这个版本,所有函数第一个参数都是ctx context.Context,之前是没有的。
Thrift安装好后,还要安装Python的Thrift模块,以及Go的Thrift包(git.apacha.org/thrift.git/lib/go/thrift)。
2、使用
我分别使用Python和Go编写了server和client,并拿一个语言当server,另一种语言做client,来测试。
在编写server和client之前,要定义一个.thrift文件,它规范了通用的服务接口,数据的格式,比如一个函数,参数是什么格式,函数返回值是什么格式。
就拿最简单的:
service Hello {
string ping(),
string talk(1: string message)
}
serivce是服务接口。关于更多的Thrift的编写可参考官网。编写好后,根据语言,生成对应的文件目录,包含不同的Processor和CLient。
thrift --gen py(go) hello.thrift
之后,开始编写server和client:
Go版本:
注释写在代码上了。
Server:
package main
import (
"git.apache.org/thrift.git/lib/go/thrift"
"rpcserver/gen-go/hello"
"fmt"
"context"
)
type HelloHandle struct {
}
//利用thrift自动生成代码中,函数必须是包含了返回值。
//job
func (h *Job) Ping(ctx context.Context) (string,error) {
return "test thrift connected using ping",nil
}
func (h *Job) GetTotal(ctx context.Context)(int64,error) {
return 100,nil
}
func (h *Job) GetSeriodTotal(ctx context.Context,start string,end string) (int64,error) {
return 10,nil
}
func main() {
jobhandler := &job.Job{}
//processor比较重要,用来从连接中读取数据,并将处理授权给handle(具体的业务逻辑代码,开发者自己编写的部分),然后将handle的处理结果写到当前的连接上。
processor := jobrpc.NewJobInterfaceProcessor(jobhandler)
//为到来的链接创建传输对象
transport,err := thrift.NewTServerSocket("localhost:7000")
if err != nil {
fmt.Println(err)
}
tfactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
//定义数据传输的协议,这里是二进制传输。除此之外,还有json等传输格式。
protocalfactory := thrift.NewTBinaryProtocolFactoryDefault()
server := thrift.NewTSimpleServer4(processor,transport,tfactory,protocalfactory)
fmt.Println("the server is started,Listening HTTP on localhost:7000")
//Go的server就是异步非阻塞的处理方式。对于FrameTranseport这种异步传输,就要选这个。
server.Serve()
fmt.Println("the server is started,Listening HTTP on localhost:7000")
}
Client:
package main
import (
"fmt"
"rpcserver/gen-go/hello"
"git.apache.org/thrift.git/lib/go/thrift"
"context"
)
func main() {
defaultctx := context.Background()
//创建socket
sock,err := thrift.NewTSocket("localhost:7000")
fmt.Println(sock,err)
//非阻塞式传输
transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
transport,err := transportFactory.GetTransport(sock)
protocol := thrift.NewTBinaryProtocolFactoryDefault()
client := jobrpc.NewJobInterfaceClientFactory(transport,protocol)
//建立连接
if err := transport.Open();err != nil {
fmt.Println(err)
}
defer transport.Close()
fmt.Println(client.Ping(defaultctx))
total,err := client.GetTotal(defaultctx)
if err == nil {
fmt.Println("Excute function in server ,and get total value:", total)
} else
{
fmt.Println(err)
}
serid_total,err := client.GetSeriodTotal(defaultctx,"2018-01-01","2018-01-31")
if err == nil {
fmt.Println("execute function GetSeriodTotal in server,and get serid total value:",serid_total)
} else {
fmt.Println(err)
}
}
defer transport.Close()
fmt.Println(client.Ping())
fmt.Println(client.Talk("hello haibo"))
}
Python版本:
Server:
import sys
sys.path.append('./gen-py')
from hello import Hello
from hello.ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class HelloWorldHandler:
def ping(self):
return "pong"
def communcate(self, mesage):
ret = "Received: " + mesage
print ret
return ret
handler = HelloWorldHandler()
processor = Hello.Processor(handler)
transport = TSocket.TServerSocket("localhost", 8000)
tfactory = TTransport.TFramedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
print "Starting thrift server in python,listening at 8000..."
server.serve()
print "done!"
Client:
import sys
sys.path.append('./gen-py')
from hello import Hello
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
transport = TSocket.TSocket('localhost', 9090)
transport = TTransport.TFramedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = Hello.Client(protocol)
transport.open()
print "server - " + client.ping()
msg = client.communcate("Hello!")
print "server - " + msg
transport.close()
我首先是分别用单一语言验证,然后启动go的server,用python的客户端连接,正常。但反过来,却出现错误,提示Python的server actively refused !!!不知道为什么。
需要学习的东西真的还有很多。
题外话:今天在运行的时候总是报错,服务端的错误必须显示返回给客户端,你在客户端执行panic会导致socket关闭,出现EOF。如果你觉得服务端哪段代码容易出现问题,应该返回对应的错误。
下午又写了一下RPC应用代码,实现一个小功能。
需求:将类型为[]map[string][]byte的数据转换为[]map[string]int64。
实现:
new_a := new([]map[string]int64)
for _,item := range result {
temp_map := map[string]int64{}
for k,v := range item {
d,err := strconv.Atoi(string(v[:]))
if err != nil {
return nil,errors.New("value is not number")
}
temp_map[k] = int64(d)
}
*new_a = append(*new_a,temp_map)
}
微信分享/微信扫码阅读