创建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)
	}

 

 

--------EOF---------
本文微信分享/扫码阅读