- 标准库的RPC默认采用Go语言特有的Gob编码,因此从其他语言调用Go语言实现的RPC服务将比较困难。在互联网的微服务时代,每个RPC以及服务的使用者都可能采用不同的编程语言,因此跨语言是互联网时代RPC的一个首要条件。得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言的。
- Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另一个是RPC建立在抽象的io.ReadWriteCloser接口之上,我们可以将RPC架设在不同的通信协议之上。这里我们尝试通过官方自带的net/rpc/jsonrpc拓展实现一个跨语言的RPC。
- 首先是基于JSO你编码重新实现RPC服务:
代码中最大的变化是用rpc.ServeCodec()函数替代了rpc.ServeConn()函数,传入的参数是针对服务器端的JSON编解码器。 - 然后是实现JSON版本的客户端:
先手工调用了net.Dial()函数建立了TCP链接,然后基于该链接建立针对客户端的JSON编解码器。 - 在确保客户端可以正常调用RPC服务的方法之后,我们用一个普通的TCP服务来代替GO语言版的RPC服务,这样可以查看客户端调用时发送的数据格式。例如,通过命令nc -l 1234在同样的端口启动一个TCP服务。然后执行一个RPC调用会发现nc输出了一下的信息:
{"method":"HelloService.Hello", "params":["hello"], "id":0}
这是一个JSON编码的数据,其中method部分对应要调用的由RPC服务和方法组合成的名字,params部分的第一个元素为参数,id是由调用方维护的唯一的调用编号。 - 请求的JSON数据对象在内部对应两个结构体:客户端是clientRequest,服务器端是serverRequest。clientRequest和serverRequest结构体的内容基本是一致的。
在获取到RPC调用对应的JSON数据后,可以通过直接向架设了RPC服务的TCP服务器发送JSON数据模拟RPC方法调用:
$ echo -e '{"method":"HelloService.Hello", "params":["hello"], "id":1}' | nc localhost 1234
返回的结果也是JSON格式的数据:
{"id":1, "result":"hello:hello", "error":null}
其中id对应输入的id参数,result为返回的结果,error部分在出问题时表示错误信息。对顺序调用来说,id不是必须的。但Go语言的RPC框架支持异步调用,当返回结果的顺序和调用的顺序不一致时,可以通过id来识别对应的调用。 - 返回的JSON数据也对应内部的两个结构体:客户端是clientResponse,服务器端是serverResponse。两个结构体的内容同样也是类似的:
- 因此,无论采用哪种语言,只要遵循同样的JSON结构,以同样的流程就可以和Go语言编写的RPC服务进行通信。这样就实现了跨语言的RPC。
6和7中的结构体在net/rpc/jsonrpc/client.go和net/rpc/jsonrpc/server.go中。