标准库的RPC默认采用Go语言特有的gob编码,因此从其它语言调用Go语言实现的RPC服务将比较困难。在互联网的微服务时代,每个RPC以及服务的使用者都可能采用不同的编程语言,因此跨语言是互联网时代RPC的一个首要条件。得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言支持的。
Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另 一个是RPC建立在抽象的io.ReadWriteCloser接口之上的,我们可以将RPC架设在不同的通讯协议之上。这里我们将尝试通过官方自带的net/rpc/jsonrpc扩展实现一个跨语言的PPC。
首先是基于json编码重新实现RPC服务:
服务端
package mainimport ("net""net/rpc""net/rpc/jsonrpc")type HelloService struct {}func (s *HelloService) Hello(request string, reply *string) error {*reply = "hello "+ requestreturn nil}func main(){rpc.RegisterName("HelloService", new(HelloService))listener, err := net.Listen("tcp", ":1234")if err != nil {panic("启动错误")}for {conn, err := listener.Accept()if err != nil {panic("接收")}go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))}}
代码中最大的变化是用rpc.ServeCodec函数替代了rpc.ServeConn函数,传入的参数是针对服务端的json编解码器。
客户端
package mainimport ("fmt""net""net/rpc""net/rpc/jsonrpc")func main(){conn, err := net.Dial("tcp", "localhost:1234")if err != nil {panic("连接错误")}client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))var reply stringerr = client.Call("HelloService.Hello", "imooc", &reply)if err != nil {panic("调用错误")}fmt.Println(reply)}
python客户端
import jsonimport socketimport itertoolsimport timeclass JSONClient(object):def __init__(self, addr):self.socket = socket.create_connection(addr)self.id_counter = itertools.count()def __del__(self):self.socket.close()def call(self, name, *params):request = dict(id=next(self.id_counter),params=list(params),method=name)self.socket.sendall(json.dumps(request).encode())# This must loop if resp is bigger than 4Kresponse = self.socket.recv(4096)response = json.loads(response.decode())if response.get('id') != request.get('id'):raise Exception("expected id=%s, received id=%s: %s"%(request.get('id'), response.get('id'),response.get('error')))if response.get('error') is not None:raise Exception(response.get('error'))return response.get('result')def close(self):self._socket.close()if __name__ == '__main__':rpc = JSONClient(("localhost", 1234))args = "hello"print(rpc.call("HelloService.Hello", args))
import jsonimport socket# 客户端 发送一个数据,再接收一个数据request = {"id":0,"params":["imooc"],"method": "HelloService.Hello"}client = socket.create_connection(("localhost", 1234))client.sendall(json.dumps(request).encode())# This must loop if resp is bigger than 4Kresponse = client.recv(4096)response = json.loads(response.decode())print(response)client.close() #关闭这个链接
