net/rpc 库
net/rpc 库是 golang 官方内置的 rpc 库
其源代码就位于: $GOROOT/src/net/rpc ,比如 /usr/local/go/src/net/rpc/
net/rpc 具有良好的代码结构,众多开源库网络层实现,均采用相同的编程模式,比如 gRPC 、 go-micro
因此不免有点小激动,记录一二
背景
看过不少 rpc 库都是使用 golang 的 reflection 库做 rpc 功能,包括 gRPC 、 go-micro 、 公司内部项目 1 rpc
很少想过直接复用 net/rpc
官方的 net/rpc 库,不深入看代码的话,可能会被定性为:
- 内置 net 库处理
- 使用 gob 编解码网络数据
因此,无法复用 net/rpc 库
实际上官方的 net/rpc 具备很好的编程模式,可以做到:
- 可以自定义网络层
- 可以自定义网络数据编解码
代码结构
+------------------+
| |
| rpc Logic |
| |
+-----+-----+------+
^ |
| |
1. get data by codec | |
| |
| | 2. put data to codec
| |
| v
+-----+-----+------+ +----------------------+
| | codec have abstract IO layer | |
| Codec +-------------------------------+ io.ReadWriteCloser |
| | | |
+------------------+ +-----------+----------+
|
+--------------------+--------------------+
| | |
| | |
+-------+--------+ +-------+--------+ +-------+--------+
| | | | | |
| net/Conn | | bytes.Buffer | | other your want|
| | | | | |
+----------------+ +----------------+ +----------------+
-
net/rpc 库的 rpc 逻辑通过 codec (编解码器),使之与 IO 层无关
-
IO 层使用了抽象的接口 io.ReadWriteCloser
type ReadWriteCloser interface { Reader Writer Closer }
- IO 层具体实现,可以是 TCP socket
- 文件 IO
- 或者内存 buffer
- 其他任何实现 io.ReadWriteCloser 的对象
-
net/rpc 库 tcp net 、gob 相关代码,都在一个文件中,因此看上去比较混乱,实际上代码结构很清晰
-
该经典编程模式在众多开源中均采用,比如 gRPC 、 go-micro
源代码分析
1. 目录文件
linux 下通常位于 /usr/local/go/src/net/rpc/
/usr/local/go/src/net/rpc/
├── client.go // rpc 客户端
├── client_test.go
├── debug.go
├── jsonrpc // 例子, 自定义网络数据编解码;也可以看做 jsonrpc
│ ├── all_test.go
│ ├── client.go
│ └── server.go
├── server.go // rpc 服务器
└── server_test.go
2. net/rpc/server.go
- 包含 RPC 注册,都是反射库的相关知识,略
- 包含 net 库初始化接收消息等,常见网络库写法,略
- gob 消息编解码,略
- 最最核心代码,如下 2 个函数:
// ServeCodec is like ServeConn but uses the specified codec to // decode requests and encode responses. func (server *Server) ServeCodec(codec ServerCodec) { // 略,与 ServeRequest 的差不多,这里就不显示了 } // ServeRequest is like ServeCodec but synchronously serves a single request. // It does not close the codec upon completion. func (server *Server) ServeRequest(codec ServerCodec) error { sending := new(sync.Mutex) service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec) if err != nil { // 错误处理,分支逻辑,略 } service.call(server, sending, nil, mtype, req, argv, replyv, codec) return nil }
server.readRequest(codec)
, 通过 codec ,获取要调用的函数名,进而获取方法
对象- server.readRequest(codec)` , 通过 codec ,同时也获取了函数参数对象
service.call
基于反射调用 rpc 方法
3. net/rpc/client.go
与 net/rpc/server.go 类似,代码上是同步调用
代码分析, 略
自定义 Codec 、 IO 的例子
笔者在理解 net/rpc 代码,同时写了下测试例子,并调试,以便更好的阅读代码,及用于其他
github 地址: https://github.com/fananchong/test_golang_rpc
个人认为,这是 golang 编写 rpc 代码的最简方式
欢迎 star