import "net/rpc"
rpc包提供了通过网络或其他I/O连接对一个对象的导出方法的访问。服务端注册一个对象,使它作为一个服务被暴露,服务的名字是该对象的类型名。注册之后,对象的导出方法就可以被远程访问。服务端可以注册多个不同类型的对象(服务),但注册具有相同类型的多个对象是错误的。
只有满足如下标准的方法才能用于远程访问,其余方法会被忽略:
- 方法是导出的 - 方法有两个参数,都是导出类型或内建类型 - 方法的第二个参数是指针 - 方法只有一个error接口类型的返回值
事实上,方法必须看起来像这样:
func (t *T) MethodName(argType T1, replyType *T2) error
其中T、T1和T2都能被encoding/gob包序列化。这些限制即使使用不同的编解码器也适用。(未来,对定制的编解码器可能会使用较宽松一点的限制)
方法的第一个参数代表调用者提供的参数;第二个参数代表返回给调用者的参数。方法的返回值,如果非nil,将被作为字符串回传,在客户端看来就和errors.New创建的一样。如果返回了错误,回复的参数将不会被发送给客户端。
服务端可能会单个连接上调用ServeConn管理请求。更典型地,它会创建一个网络监听器然后调用Accept;或者,对于HTTP监听器,调用HandleHTTP和http.Serve。
想要使用服务的客户端会创建一个连接,然后用该连接调用NewClient。
更方便的函数Dial(DialHTTP)会在一个原始的连接(或HTTP连接)上依次执行这两个步骤。
生成的Client类型值有两个方法,Call和Go,它们的参数为要调用的服务和方法、一个包含参数的指针、一个用于接收接个的指针。
Call方法会等待远端调用完成,而Go方法异步的发送调用请求并使用返回的Call结构体类型的Done通道字段传递完成信号。
除非设置了显式的编解码器,本包默认使用encoding/gob包来传输数据。
这是一个简单的例子。一个服务端想要导出Arith类型的一个对象:
package server 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 }
服务端会调用(用于HTTP服务):
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)
此时,客户端可看到服务"Arith"及它的方法"Arith.Multiply"、"Arith.Divide"。要调用方法,客户端首先呼叫服务端:
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") if err != nil { log.Fatal("dialing:", err) }
然后,客户端可以执行远程调用:
// Synchronous call args := &server.Args{7,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", args.A, args.B, reply)
或:
// Asynchronous call quotient := new(Quotient) divCall := client.Go("Arith.Divide", args, quotient, nil) replyCall := <-divCall.Done // will be equal to divCall // check errors, print, etc.
服务端的实现应为客户端提供简单、类型安全的包装。
const ( // HandleHTTP使用的默认值 DefaultRPCPath = "/_goRPC_" DefaultDebugPath = "/debug/rpc" )
var DefaultServer = NewServer()
DefaultServer是*Server的默认实例,本包和Server方法同名的函数都是对其方法的封装。
var ErrShutdown = errors.New("connection is shut down")
type ServerError string
ServerError represents an error that has been returned from the remote side of the RPC connection.
func (e ServerError) Error() string
type Request struct { ServiceMethod string // 格式:"Service.Method" Seq uint64 // 由客户端选择的序列号 // 内含隐藏或非导出字段 }
Request是每个RPC调用请求的头域。它是被内部使用的,这里的文档用于帮助debug,如分析网络拥堵时。
type Response struct { ServiceMethod string // 对应请求的同一字段 Seq uint64 // 对应请求的同一字段 Error string // 可能的错误 // 内含隐藏或非导出字段 }
Response是每个RPC调用回复的头域。它是被内部使用的,这里的文档用于帮助debug,如分析网络拥堵时。
type ClientCodec interface { // 本方法必须能安全的被多个go程同时使用 WriteRequest(*Request, interface{}) error ReadResponseHeader(*Response) error ReadResponseBody(interface{}) error Close() error }
ClientCodec接口实现了RPC会话的客户端一侧RPC请求的写入和RPC回复的读取。客户端调用WriteRequest来写入请求到连接,然后成对调用ReadRsponseHeader和ReadResponseBody以读取回复。客户端在结束该连接的事务时调用Close方法。ReadResponseBody可以使用nil参数调用,以强制回复的主体被读取然后丢弃。
type ServerCodec interface { ReadRequestHeader(*Request) error ReadRequestBody(interface{}) error // 本方法必须能安全的被多个go程同时使用 WriteResponse(*Response, interface{}) error Close() error }
ServerCodec接口实现了RPC会话的服务端一侧RPC请求的读取和RPC回复的写入。服务端通过成对调用方法ReadRequestHeader和ReadRequestBody从连接读取请求,然后调用WriteResponse来写入回复。服务端在结束该连接的事务时调用Close方法。ReadRequestBody可以使用nil参数调用,以强制请求的主体被读取然后丢弃。
type Call struct { ServiceMethod string // 调用的服务和方法的名称 Args interface{} // 函数的参数(下层为结构体指针) Reply interface{} // 函数的回复(下层为结构体指针) Error error // 在调用结束后,保管错误的状态 Done chan *Call // 对其的接收操作会阻塞,直到远程调用结束 }
Call类型代表一个执行中/执行完毕的RPC会话。
type Client struct {
// 内含隐藏或非导出字段
}
Client类型代表RPC客户端。同一个客户端可能有多个未返回的调用,也可能被多个go程同时使用。
func NewClient(conn io.ReadWriteCloser) *Client
NewClient返回一个新的Client,以管理对连接另一端的服务的请求。它添加缓冲到连接的写入侧,以便将回复的头域和有效负载作为一个单元发送。
func NewClientWithCodec(codec ClientCodec) *Client
NewClientWithCodec类似NewClient,但使用指定的编解码器,以编码请求主体和解码回复主体。
func Dial(network, address string) (*Client, error)
Dial在指定的网络和地址与RPC服务端连接。
func DialHTTP(network, address string) (*Client, error)
DialHTTP在指定的网络和地址与在默认HTTP RPC路径监听的HTTP RPC服务端连接。
func DialHTTPPath(network, address, path string) (*Client, error)
DialHTTPPath在指定的网络、地址和路径与HTTP RPC服务端连接。
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error
Call调用指定的方法,等待调用返回,将结果写入reply,然后返回执行的错误状态。
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call
Go异步的调用函数。本方法Call结构体类型指针的返回值代表该次远程调用。通道类型的参数done会在本次调用完成时发出信号(通过返回本次Go方法的返回值)。如果done为nil,Go会申请一个新的通道(写入返回值的Done字段);如果done非nil,done必须有缓冲,否则Go方法会故意崩溃。
func (client *Client) Close() error
type Server struct {
// 内含隐藏或非导出字段
}
Server代表RPC服务端。
func NewServer() *Server
NewServer创建并返回一个*Server。
func (server *Server) Register(rcvr interface{}) error
Register在server注册并公布rcvr的方法集中满足如下要求的方法:
- 方法是导出的 - 方法有两个参数,都是导出类型或内建类型 - 方法的第二个参数是指针 - 方法只有一个error接口类型的返回值
如果rcvr不是一个导出类型的值,或者该类型没有满足要求的方法,Register会返回错误。Register也会使用log包将错误写入日志。客户端可以使用格式为"Type.Method"的字符串访问这些方法,其中Type是rcvr的具体类型。
func (server *Server) RegisterName(name string, rcvr interface{}) error
RegisterName类似Register,但使用提供的name代替rcvr的具体类型名作为服务名。
func (server *Server) Accept(lis net.Listener)
Accept接收监听器l获取的连接,然后服务每一个连接。Accept会阻塞,调用者应另开线程:"go server.Accept(l)"
func (server *Server) ServeConn(conn io.ReadWriteCloser)
ServeConn在单个连接上执行server。ServeConn会阻塞,服务该连接直到客户端挂起。调用者一般应另开线程调用本函数:"go server.ServeConn(conn)"。ServeConn在该连接使用gob(参见encoding/gob包)有线格式。要使用其他的编解码器,可调用ServeCodec方法。
func (server *Server) ServeCodec(codec ServerCodec)
ServeCodec类似ServeConn,但使用指定的编解码器,以编码请求主体和解码回复主体。
func (server *Server) ServeRequest(codec ServerCodec) error
ServeRequest类似ServeCodec,但异步的服务单个请求。它不会在调用结束后关闭codec。
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP实现了回应RPC请求的http.Handler接口。
func (server *Server) HandleHTTP(rpcPath, debugPath string)
HandleHTTP注册server的RPC信息HTTP处理器对应到rpcPath,注册server的debug信息HTTP处理器对应到debugPath。HandleHTTP会注册到http.DefaultServeMux。之后,仍需要调用http.Serve(),一般会另开线程:"go http.Serve(l, nil)"
func Register(rcvr interface{}) error
Register在DefaultServer注册并公布rcvr的方法。
func RegisterName(name string, rcvr interface{}) error
RegisterName函数类似Register函数,但使用提供的name代替rcvr的具体类型名作为服务名。
func Accept(lis net.Listener)
Accept接收监听器l获取的连接,然后将每一个连接交给DefaultServer服务。Accept会阻塞,调用者应另开线程:"go server.Accept(l)"
func ServeConn(conn io.ReadWriteCloser)
ServeConn在单个连接上执行DefaultServer。ServeConn会阻塞,服务该连接直到客户端挂起。调用者一般应另开线程调用本函数:"go ServeConn(conn)"。ServeConn在该连接使用gob(参见encoding/gob包)有线格式。要使用其他的编解码器,可调用ServeCodec方法。
func ServeCodec(codec ServerCodec)
ServeCodec类似ServeConn,但使用指定的编解码器,以编码请求主体和解码回复主体。
func ServeRequest(codec ServerCodec) error
ServeRequest类似ServeCodec,但异步的服务单个请求。它不会在调用结束后关闭codec。
func HandleHTTP()
HandleHTTP函数注册DefaultServer的RPC信息HTTP处理器对应到DefaultRPCPath,和DefaultServer的debug处理器对应到DefaultDebugPath。HandleHTTP函数会注册到http.DefaultServeMux。之后,仍需要调用http.Serve(),一般会另开线程:"go http.Serve(l, nil)"