Go原生RPC
官方地址: https://pkg.go.dev/net/rpc
RPC包简介
rpc包实现了通过网络或者别的io连接,来远程访问对象的导出方法。对象以其类型名作为标识注册为可访问的服务。注册之后,对象的导出方法将可以远程访问。一个服务可以注册多个不同类型的对象,但是注册多个相同类型的对象是错误的。
只有满足这些标准的方法才可以被远端访问,别的方法会被忽略:
- 对象是导出的
- 方法是导出的
- 方法有两个参数,均是导出(或内置)类型
- 方法的第二个参数是一个指针
- 方法有一个错误返回值
实际上,方法应该看起来像这样:
func (t *T) MethodName(argType T1, replyType *T2) error
其中T1和T2可以被encoding/gob编码。即便使用不同的编解码器,这些要求也同样适用(在将来,这些要求可能针对自定义编解码器会有所缓和)。
方法的第一个参数表示调用者的入参,第二个参数表示返回的出参。方法的返回值(如果不是nil),可以认为是由errors.New创建的。如果方法返回错误,出参将不会返回。
在创建客户端原始网络连接上,希望使用该服务的客户端会建立了一个连接,然后在这个连接上调用NewClient,现在Dial方法封装了这两步。生成的客户端对象有两个方法,Call和Go。调用时,需要指定服务和方法,然后两个指针参数,一个指针包含请求参数,另一个指针保存返回结果。
Call方法同步等待远程方法执行完成,而Go方法异步启动调用并使用Call结构体的Done管道发出完成信号。
除非显式设置编解码器,默认使用encoding/gob传输数据。
例子
这里有一个简单的例子,服务端希望导出Arith对象,客户端调用其Multiply方法
服务端
服务端基于http提供服务,暴露了Arith.Multiply和Arith.Divide两个rpc接口供客户端调用
package server
import (
"errors"
"log"
"net"
"net/http"
"net/rpc"
"time"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func StartServer() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
time.Sleep(time.Second)
}
客户端
客户端同步调用Arith.Multiply方法
package main
import (
"demo/com.my/rpc/server"
"fmt"
"log"
"net/rpc"
"time"
)
func main() {
server.StartServer()
serverAddress := "127.0.0.1"
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
log.Fatal("dialing:", err)
}
// Synchronous call
args := &server.Args{A: 7, B: 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
time.Sleep(time.Hour)
}
连接分析
rpc.HandleHTTP
rpc在http通道上注册了两个路由,DefaultRPCPath和DefaultDebugPath,前者用于传输rpc消息,后者是一个简单的rpc调用统计页面
rpc.DialHTTP
客户端建立连接时指定路径,DialHTTPPath(network, address, DefaultRPCPath)