rpc服务器也就是在tcp服务器的基础上加上自定义的rpc协议而已。一个rpc协议里,主要有个3个非常重要的信息。
· 调用的远程method名字,一般就是一个函数名
· call参数,也就是发送给服务器的数据
· 客户端生成的调用请求seq
首先看看官方给出的使用例子:看看rpc.Register到底做了什么事情!
type Args struct {
A, B int
}
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
arith := new(Arith)
rpc.Register(arith)
找到regsiter主要函数如下所示:主要是把结构体加入到Server结构体中serverMap中, 初始化工作,赋值方法等操作。
func (server *Server) register(rcvr interface{}, name string, useName bool) error {
s := new(service)
。。。。。。。。。。。。。。。。
server.serviceMap[s.name] = s
return nil
}
那么rpc服务器如何能够根据method去调用对应的方法呢?Go语言在这里其实采用反射的手段,虽然表面上是注册的是对象,实际却是通过反射取得了对象的所有方法,然后采用了map表保存了method到方法的映射
type Server struct {
mu sync.RWMutex // protects the serviceMap
serviceMap map[string]*service
reqLock sync.Mutex // protects freeReq
freeReq *Request
respLock sync.Mutex // protects freeResp
freeResp *Response
}
rpc的service包括方法名、方法反射,类型等
type service struct {
name string // name of service
rcvr reflect.Value // receiver of methods for the service
typ reflect.Type // type of the receiver
method map[string]*methodType // registered methods
}
// A ServerCodec implements reading of RPC requests and writing of
// RPC responses for the server side of an RPC session.
// The server calls ReadRequestHeader and ReadRequestBody in pairs
// to read requests from the connection, and it calls WriteResponse to
// write a response back. The server calls Close when finished with the
// connection. ReadRequestBody may be called with a nil
// argument to force the body of the request to be read and discarded.
type ServerCodec interface {
ReadRequestHeader(*Request) error
ReadRequestBody(interface{}) error
// WriteResponse must be safe for concurrent use by multiple goroutines.
WriteResponse(*Response, interface{}) error
Close() error
}
ReadRequestHeader和ReadRequestBody一起使用从connection, nil读取body取出并丢弃. 最终使用的都是这几个函数,读取和发送response
只需要实现ServerCodec这个接口,就可以自定义服务端的编码解码器,实现自定义的rpc协议了。 Go rpc服务器端和客户端都是默认使用的Gob序列化协议数据。
func (server *Server) Accept(lis net.Listener) {
for {
conn, err := lis.Accept()
if err != nil {
log.Print("rpc.Serve: accept:", err.Error())
return
}
go server.ServeConn(conn)
}
}
Accept用来处理一个监听器,一直在监听客户端的连接,一旦监听器接收了一个连接,则还是交给 ServeConn 在另外一个goroutine中去处理:
// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection. To use an alternate codec, use ServeCodec.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
buf := bufio.NewWriter(conn)
srv := &gobServerCodec{
rwc: conn,
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(buf),
encBuf: buf,
}
server.ServeCodec(srv)
}
// 根据指定的codec进行协议解析
// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (server *Server) ServeCodec(codec ServerCodec) {
sending := new(sync.Mutex)
for {
service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
if err != nil {
if debugLog && err != io.EOF {
log.Println("rpc:", err)
}
if !keepReading {
break
}
// send a response if we actually managed to read a header.
if req != nil {
server.sendResponse(sending, req, invalidRequest, codec, err.Error())
server.freeRequest(req)
}
continue
}
go service.call(server, sending, mtype, req, argv, replyv, codec)
}
codec.Close()
}
解码request和编码response
func (server *Server) readRequestHeader(codec ServerCodec)
读取头部信息,获取service和方法。
func (s *service) call(server *Server, sending *sync.Mutex, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {
mtype.Lock()
mtype.numCalls++
mtype.Unlock()
function := mtype.method.Func
// Invoke the method, providing a new value for the reply.
returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})
// The return value for the method is an error.
errInter := returnValues[0].Interface()
errmsg := ""
if errInter != nil {
errmsg = errInter.(error).Error()
}
server.sendResponse(sending, req, replyv.Interface(), codec, errmsg)
server.freeRequest(req)
}
returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})