一个简易 RPC 框架
RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许调用不同进程空间的程序。
一般和 RPC 进行对比的是基于 HTTP 协议的 Restful 框架。HTTP 协议一般来讲有更好可读性,相对 RPC 来讲,有一些缺点
- HTTP 报文冗余,有很多无用信息,RPC 可以自定义格式,
- RPC 可以采用高效的序列化协议,用二进制传输,获取更高的性能,
- RPC 更灵活,可以在其上实现一些注册中心、负载均衡等功能
假设客户端与服务端进行通信,客户端不关心服务端机器的部署信息,只关注自己的请求是否得到需要的结果。因此,在 RPC 框架中,需要实现注册中心和负载均衡,在业务侧提供一些公用能力,如消息编解码、连接池、收发线程、超时处理等
- 服务端与消息编码
- 高性能客户端
- 服务注册
- 超时处理
- 支持HTTP协议
- 负载均衡
- 服务发现与注册中心
主要实现服务端接收消息的基本框架,主要包括
- 读取请求 readRequest
- 处理请求 handleRequest
- 回复请求 sendResponse
读取以及回复需要编解码,框架允许一次接收多个请求,而处理请求是异步的,因此回复请求时需要加锁,因为不知道处理请求什么时候处理完,而一次只能回复一个请求
RPC 的客户端主要实现两个功能,一个是包装请求并发送出去,另一个是接收服务端的响应;除此之外,还需要实现供用户自定义的服务端连接接口,比如指定服务端 IP
- 发送请求 加发送锁,记录一下该 call 在客户端缓存中,格式为 map[uint64]*Call,key 为 seq
如果发生错误,调用 call.done,移出缓存中的 call;未发生错误时就编码发送
- 接收响应 for 循环一直接收响应,直到有错误产生,客户端起一个协程即可;
循环内部,解码 header,如果有错误就处理错误;无错时解码 body 得到 reply
对 net/rpc 而言,一个函数需要能够被远程调用,需要满足如下五个条件:
- the method’s type is exported. – 方法所属类型是导出的。
- the method is exported. – 方式是导出的。
- the method has two arguments, both exported (or builtin) types. – 两个入参,均为导出或内置类型。
- the method’s second argument is a pointer. – 第二个入参必须是一个指针。
- the method has return type error. – 返回值为 error 类型。
通过 service.methodname 可以知道对应的 method,为了不重复匹配不同的 method,需要用反射获取某个结构体的所有方法,并且能够通过方法,获取到该方法所有的参数类型与返回值。
需要客户端处理超时的地方有:
- 与服务端建立连接,导致的超时
- 发送请求到服务端,写报文导致的超时
- 等待服务端处理时,等待处理导致的超时(比如服务端已挂死,迟迟不响应)
- 从服务端接收响应时,读报文导致的超时
需要服务端处理超时的地方有:
- 读取客户端请求报文时,读报文导致的超时
- 发送响应报文时,写报文导致的超时
- 调用映射服务的方法时,处理报文导致的超时
在代码中,即客户端与服务端连接时,需要超时处理;客户端 call 调用服务端由于种种原因超时;服务端处理 call 函数超时需要处理
框架中可以在 连接 或者 call 时, 启用协程,使用 channel 获取结果,用 select 阻塞等待(1. 结果返回;2. 超时退出);这里需要注意,超时需要把协程也干掉,否则会有 goroutine 泄露问题,因为 goroutine 将结果放到 channel 里,却一直没人拿了。