Go net/rpc 包总结

来源:互联网 发布:网络打鱼秘籍 编辑:程序博客网 时间:2024/05/23 19:17

RPC

rpc远程过程调用,go net/rpc包提供了通过网络访问一个对象的方法的能力。服务器需要注册对象, 通过对象的类型名暴露这个服务。注册后这个对象的输出方法就可以远程调用,这个库封装了底层传输的细节,包括序列化。服务器可以注册多个不同类型的对象,但是注册相同类型的多个对象的时候回出错。
通过阅读net/rpc包你可以学到互斥锁、goroutine、map、channel、interface、反射等相关知识。

Clinet.go

阅读源码我们可以得知连接分为
DialHTTP —— 连接到指定的网络地址http RPC服务器,监听默认的HTTP RPC路径
DialHTTPPath —— 连接在指定的网络地址和路径HTTP RPC服务器
NewClient —— 返回一个新的客户端句柄,在链接的有效期内链接服务端发送请求
NewClientWithCodec——与NewClient相似,但是它使用指定得编码器编码请求,解码返回
我们可以看出DialHTTP与DialHTTPPath可以归为一组,NewClient与NewClientWithCodec可以归为一组。
DialHTTP 和 DialHTTPPath 是通过HTTP的方式和服务器建立连接,他俩的区别之在于是否设置上下文路径。
NewClient使用默认编码NewClientWithCodec使用其它编码。
Clinet端比较重要的input方法。
从代码中我们可以看出其在一个for循环中首先读取了头,然后从map中获取值若为nil则调用了ReadResponseBody方法并传入了nil迫使放弃读取。这也印证了代码中所说的ReadResponseHeader与ReadResponseBody为成对调用的。
另一个为异步调用的Go异步调用函数。它返回call结构体。done的通道将在调用完成时发出相同的调用对象来发出信号。如果done为nil,Go将会分配一个新的channel。如果non-nil,done必须有缓存否则会崩溃。
故Client端在使用时有如下注意事项:
1. channel必须为有缓存的
2. ReadResponseHeader与ReadResponseBody配合使用
3. 客户端在一个链接结束后调用Close关闭
4. 若ReadResponseHeader失败则需要调用ReadResponseBody并传入nil迫使放弃。

Server.go

在服务端注册并发布方法需要满足一下条件:

  1. 导出的方法和导出的类型
  2. 两个参数都是导出的类型
  3. 第二个参数必须为指针
  4. 返回值为一个错误类型

注册方法:

Register与RegisterName,RegisterName与Register类似,但是使用传入的名字作为类型,代替接收者的具体类型。

监听

Accept—Accept接收监听的连接。并为每一个进入的连接提供请求
HandleHTTP—HandleHTTP再RPCPath注册一个HTTP handler作为RPC的消息。它必须调用http.Serve(),再go语句中调用
ServeCodec—运行这个server在一个单独连接
ServeConn—为连接提供服务直到客户端挂起,调用者使用go ServeConn调用。 ServeConn在连接中使用gob包进行写格式化
ServeHTTP—ServeHTTP执行http.Handler应答RPC请求
ServeRequest—就像是ServeCodec但是同步服务的请求。它在完成后不关闭编解码器

简单的示例

clinet.go

package mainimport (    "net/rpc"    "fmt")const (    PORT = "127.0.0.1:10012"    PROXY = "tcp")type Hello struct {    StrHello string}type ResStr struct {    StrHi string}func main() {    client, err := rpc.DialHTTP(PROXY, PORT)    if err != nil {        fmt.Println("dialing:", err)    }    var args = Hello{" Test rpc",}    var reply string    err = client.Call("Say.SayHello", args, &reply)    if err != nil {         fmt.Println("arith error:", err)    }    fmt.Println(reply)    args = Hello{" Go invokes the function asynchronously",}    res := new(ResStr)    resCall  := client.Go("Say.SayHi",args,&res,nil) //异步调用    replyCall := <-resCall.Done    if replyCall.Error != nil {        fmt.Println("arith error:", replyCall.Error)    }    fmt.Println(res.StrHi)}

server.go

package mainimport (    "net/rpc"    "net"    "fmt"    "net/http")const (    SAY = "Hello"    PORT = ":10012"    PROXY = "tcp")type Hello struct {    StrHello string}type ResStr struct {    StrHi string}type Say intfunc (this *Say) SayHello(args *Hello, reply *string) error {    *reply = SAY + args.StrHello    return nil}func (this *Say) SayHi(args *Hello, reply *ResStr) error {    reply.StrHi = SAY + args.StrHello    return nil}func main() {    hi := new(Say)    rpc.Register(hi)    rpc.HandleHTTP()    l, e := net.Listen(PROXY,PORT)    if e != nil {        fmt.Println("listen error:", e)    }    http.Serve(l, nil)}