标准库的RPC默认采用Go语言特有的gob编码,因此从其它语言调用Go语言实现的RPC服务将比较困难。在互联网的微服务时代,每个RPC以及服务的使用者都可能采用不同的编程语言,因此跨语言是互联网时代RPC的一个首要条件。得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言支持的。
Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另 一个是RPC建立在抽象的io.ReadWriteCloser接口之上的,我们可以将RPC架设在不同的通讯协议之上。这里我们将尝试通过官方自带的net/rpc/jsonrpc扩展实现一个跨语言的PPC。
首先是基于json编码重新实现RPC服务:

服务端

  1. package main
  2. import (
  3. "net"
  4. "net/rpc"
  5. "net/rpc/jsonrpc"
  6. )
  7. type HelloService struct {}
  8. func (s *HelloService) Hello(request string, reply *string) error {
  9. *reply = "hello "+ request
  10. return nil
  11. }
  12. func main(){
  13. rpc.RegisterName("HelloService", new(HelloService))
  14. listener, err := net.Listen("tcp", ":1234")
  15. if err != nil {
  16. panic("启动错误")
  17. }
  18. for {
  19. conn, err := listener.Accept()
  20. if err != nil {
  21. panic("接收")
  22. }
  23. go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
  24. }
  25. }

代码中最大的变化是用rpc.ServeCodec函数替代了rpc.ServeConn函数,传入的参数是针对服务端的json编解码器。

客户端

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "net/rpc"
  6. "net/rpc/jsonrpc"
  7. )
  8. func main(){
  9. conn, err := net.Dial("tcp", "localhost:1234")
  10. if err != nil {
  11. panic("连接错误")
  12. }
  13. client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
  14. var reply string
  15. err = client.Call("HelloService.Hello", "imooc", &reply)
  16. if err != nil {
  17. panic("调用错误")
  18. }
  19. fmt.Println(reply)
  20. }

python客户端

  1. import json
  2. import socket
  3. import itertools
  4. import time
  5. class JSONClient(object):
  6. def __init__(self, addr):
  7. self.socket = socket.create_connection(addr)
  8. self.id_counter = itertools.count()
  9. def __del__(self):
  10. self.socket.close()
  11. def call(self, name, *params):
  12. request = dict(id=next(self.id_counter),
  13. params=list(params),
  14. method=name)
  15. self.socket.sendall(json.dumps(request).encode())
  16. # This must loop if resp is bigger than 4K
  17. response = self.socket.recv(4096)
  18. response = json.loads(response.decode())
  19. if response.get('id') != request.get('id'):
  20. raise Exception("expected id=%s, received id=%s: %s"
  21. %(request.get('id'), response.get('id'),
  22. response.get('error')))
  23. if response.get('error') is not None:
  24. raise Exception(response.get('error'))
  25. return response.get('result')
  26. def close(self):
  27. self._socket.close()
  28. if __name__ == '__main__':
  29. rpc = JSONClient(("localhost", 1234))
  30. args = "hello"
  31. print(rpc.call("HelloService.Hello", args))
  1. import json
  2. import socket# 客户端 发送一个数据,再接收一个数据
  3. request = {
  4. "id":0,
  5. "params":["imooc"],
  6. "method": "HelloService.Hello"
  7. }
  8. client = socket.create_connection(("localhost", 1234))
  9. client.sendall(json.dumps(request).encode())
  10. # This must loop if resp is bigger than 4K
  11. response = client.recv(4096)
  12. response = json.loads(response.decode())
  13. print(response)
  14. client.close() #关闭这个链接