环境:
Golang:go1.18.2 linux/amd64
RPC
为了有直观的认识,下面是通过json将request和response进行编解码后获取的信息
// request
{"method":"QueryService.GetAge","params":["bar"],"id":0}
// response
{"id":0,"result":"The age of bar is 20","error":null}
2. 实践
/net/rpc
2.1 服务端
2.1.1 首先新建项目RPC,并创建Server目录,新建main.go
[root@tudou workspace]# mkdir -p RPC/Server && cd RPC/Server && touch main.go
2.1.2 新建一个字典,保存用户信息
// 用户信息
var userinfo = map[string]int{
"foo": 18,
"bar": 20,
}
QueryGetAge
// 实现查询服务,结构体Query实现了GetAge方法
type Query struct {
}
func (q *Query) GetAge(req string, res *string) error {
*res = fmt.Sprintf("The age of %s is %d", req, userinfo[req])
return nil
}
注:
GetAgereqresGetAge
2.1.4 注册服务的方法,并开启监听,等待rpc客户端的请求
func main() {
// 注册服务方法
if err := rpc.RegisterName("QueryService", new(Query)); err != nil {
log.Println(err)
}
// 开启监听,接受来自rpc客户端的请求
listener, _ := net.Listen("tcp", ":1234")
rpc.Accept(listener)
}
RegisterNameRegister
func (server *Server) register(rcvr any, name string, useName bool) error {
s := new(service)
s.typ = reflect.TypeOf(rcvr)
s.rcvr = reflect.ValueOf(rcvr)
sname := reflect.Indirect(s.rcvr).Type().Name()
if useName {
sname = name
}
2.1.5 运行服务
[root@tudou Server]# go build main.go && ./main
2.2 客户端
2.2.1在RPC/Server同级目录下创建Client目录,新建main.go
[root@tudou workspace]# mkdir -p RPC/Client && cd RPC/Client && touch main.go
rpc.Dialnet.Dialgob
client, _ := rpc.Dial("tcp", ":1234")
可以查看如下源码
// Dial connects to an RPC server at the specified network address.
func Dial(network, address string) (*Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return NewClient(conn), nil
}
func NewClient(conn io.ReadWriteCloser) *Client {
encBuf := bufio.NewWriter(conn)
client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
return NewClientWithCodec(client)
}
Call
var res string
err := client.Call("QueryService.GetAge", "bar", &res)
if err != nil {
log.Println(err)
}
fmt.Println(res)
2.2.4 运行客户端,得到如下结果
[root@tudou Client]# go run main.go
The age of bar is 20
3. json序列化
本来打算用gob作编解码器显示下序列化后得到的数据,奈何笔者技术不到家,得到的结果不够清晰明了,所以下面就用json作编解码器作个演示,本质是一样的。(可以参考另一篇文章【Golang | RPC】利用json编解码器实现RPC)
首先利用nc工具监听1234端口,作为服务端,获取传入的数据
[root@tudou ~]# nc -l 1234
然后运行客户端,可以发现nc工具捕获到如下数据,这就是客户端利用json编码后发送给服务端的数据
{"method":"QueryService.GetAge","params":["bar"],"id":0}
同样,nc工具可以作为客户端,向RPC服务端发送数据,得到如下返回结果
[root@tudou ~]# echo -e '{"method":"QueryService.GetAge","params":["bar"],"id":0}' | nc localhost 1234
{"id":0,"result":"The age of bar is 20","error":null}
4. 总结
- RPC是一个C/S架构,通过指定的编解码器发送request,获取response
- 服务的方法名必须可导出,即首字母要大写;方法的第一个参数类型必须可导出,即首字母大写或内置类型, 方法的第二个参数类型也必须可导出,同时为指针类型
完整代码:
https://github.com/WanshanTian/GolangLearning
参考 GolangLearning/RPC/gobRPC目录