RPC
这里send需要上锁是是因为一个rpcClient可以支持并发发送请求。pending这个map是用来存目前client正在处理的call,其中key用seq来标记,seq单调递增,这个seq类似在对client端的请求编号
register & call
server端就是要根据service,method以及对应的请求参数来执行一次远程调用,并返回响应。如何注册一个服务,如何从注册服务中找到要执行的方法,go-RPC是通过反射提供的类型信息完成的。
下面Server的数据结构,serviceMap用于保存所有注册的服务。其中service保留了服务名,服务类型以及服务实例,服务实例通常是指针的值。method中保留了服务对外Export的方法,以及方法名,参数和响应类型
原生 RPC 的坑
对 Dial 和 编码的超时处理避免阻塞
-
rpc包里的rpc.Dial函数没有timeout, 系统默认是没有timeout的,所以在这里可能卡住.所以我们可以采用net包里的 net.DialTimeout函数.
-
rpc包里默认使用gobCodec来编码解码, 这里io可能会卡住而不返回错误,所以我们要自己编写加入timeout的codec. 注意server这边读写都有timeout,但是client这边只有写有timeout,因为读的话并不能预知任务完成的时间. 于是就有了接下来这个版本的rpc,几十万个任务下来没有任何问题.
深入 go RPC 标准库
// 一个 Service 对象就是一个服务(包括方法,参数类型和返回值)
type Service struct {
Method reflect.Method
ArgType reflect.Type
ReplyType reflect.Type
}
// 多个服务的集合组织在 Server 里面,使用 map 方便查找顺便去重,并且加锁保护
// 很容易联想到 client.Call("A.Add", ...) 中间提供了查找的信息
type Server struct {
ServiceMap map[string]map[string]*Service
serviceLock sync.Mutex
}
// 使用自定义的序列化和反序列化方式来将传递的对象转成能够在网络上传输的字节流
// 使用 net.Conn 来发送和接收 TCP 字节流
流程
1. 建立TCP连接
2. 客户端调用 call
3. 客户端拿到返回值