Golang 提供了一个开箱即用的RPC服务,实现方式简约而不简单。本文对net/rpc 包做深度解读和学习实战。
RPC 简单介绍
远程过程调用 (Remote Procedure Call,RPC) 是一种计算机通信协议。允许运行在一台计算机的程序调用另一个地址空间的子程序(一般是开放网络中的一台计算机),而程序员就像调用调用本地程序一样,无需额外做交互编程。RPC 是一种 CS (Client-Server) 架构的模式,通过发送请求-接收响应的方式进行信息的交互。
net/rpc
Golang 的实现
rpc 是 cs 架构,所以既有客户端,又有服务端。下面,我们先分析通信的编码,之后从服务端、客户端角度分析RPC的实现。
通信编码
golang 在rpc 实现中,抽象了协议层,我们可以自定义协议实现我们自己的接口。如下是协议的接口:
// 服务端
type ServerCodec interface {
ReadRequestHeader(*Request) error
ReadRequestBody(interface{}) error
WriteResponse(*Response, interface{}) error
// Close can be called multiple times and must be idempotent.
Close() error
}
// 客户端
type ClientCodec interface {
WriteRequest(*Request, interface{}) error
ReadResponseHeader(*Response) error
ReadResponseBody(interface{}) error
Close() error
}
而包中提供了基于gob 二进制编码的编解码实现。当然我们也可以实现自己想要的编解码方式。
Server 端实现
结构定义
type Server struct {
serviceMap sync.Map // 保存Service
reqLock sync.Mutex // 读请求的锁
freeReq *Request
respLock sync.Mutex // 写响应的锁
freeResp *Response
}
server端通过互斥锁的方式支持了并发执行。由于每个请求和响应都需要定义Request/Response 对象,为了减少内存的分配,这里使用了一个freeReq/freeResp 链表实现了两个对象池。当需要Request 对象时,从 freeReq 链表中获取,当使用完毕后,再放回链表中。