Golang RPC 实践

easyhappy · · 661 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

> 摘要: 总体上来说,HTTP每次请求比较浪费资源的。虽然HTTP也是走在TCP上面的,但是HTTP请求自己添加了很多自己的信息,因此会消耗带宽资源。所以一些公司就是用RPC作为内部应用的通信协议。[原文](http://goguider.cn/blogs/easyhappy/articles/2019/10/13/1570930190137) 如果你对Go感兴趣, 可以关注我的公众号: GoGuider ### RPC RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。RPC协议构建于TCP或UDP,或者是HTTP上。 在Go中,标准库提供的net/rpc包实现了RPC协议需要的相关细节,开发者可以很方便的使用该包编写RPC的服务端和客户端程序。 ![](https://images.xiaozhuanlan.com/photo/2019/6acf4edd8625cf490692415ad179ce26.png) 从上图看, RPC本身就是一个client-server模型。 下面列举一个实例代码, 来了解RPC调用过程 ### server.go ```go package main import ( "fmt" "log" "net" "net/http" "net/rpc" "os" "time" ) type Args struct { A, B int } type Math int //计算乘积 func (t *Math) Multiply(args *Args, reply *int) error { time.Sleep(time.Second * 3) //睡1秒,同步调用会等待,异步会先往下执行 *reply = args.A * args.B fmt.Println("Multiply") return nil } //计算和 func (t *Math) Sum(args *Args, reply *int) error { time.Sleep(time.Second * 3) *reply = args.A + args.B fmt.Println("Sum") return nil } func main() { //创建对象 math := new(Math) //rpc服务注册了一个Math对象 公开方法供客户端调用 rpc.Register(math) //指定rpc的传输协议 这里采用http协议作为rpc调用的载体 也可以用rpc.ServeConn处理单个连接请求 rpc.HandleHTTP() l, e := net.Listen("tcp", ":1234") if e != nil { log.Fatal("listen error", e) } go http.Serve(l, nil) os.Stdin.Read(make([]byte, 1)) } ``` ### client.go ``` package main import ( "fmt" "log" "net/rpc" "time" ) type Args struct { A, B int } func main() { //调用rpc服务端提供的方法之前,先与rpc服务端建立连接 client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234") if err != nil { log.Fatal("dialHttp error", err) return } //同步调用服务端提供的方法 args := &Args{7, 8} var reply int //可以查看源码 其实Call同步调用是用异步调用实现的。后续再详细学习 err = client.Call("Math.Multiply", args, &reply) //这里会阻塞三秒 if err != nil { log.Fatal("call Math.Multiply error", err) } fmt.Printf("Multiply:%d*%d=%d\n", args.A, args.B, reply) //异步调用 var sum int divCall := client.Go("Math.Sum", args, &sum, nil) //使用select模型监听通道有数据时执行,否则执行后续程序 for { select { case <-divCall.Done: fmt.Printf("%d+%d是%d, 退出执行!", args.A, args.B, sum) return default: fmt.Println("继续等待....") time.Sleep(time.Second * 1) } } } ``` ### 运行命令 ```go go run server.go go run client.go ``` ### 运行结果 ``` Multiply:7*8=56 继续等待.... 继续等待.... 继续等待.... 7+8=15,出执行 ``` ### 调用过程解析 server端 - rpc服务注册了一个Math对象 公开方法供客户端调用 - 采用http协议作为rpc调用的载体, 处理请求 client端 - 调用rpc服务端提供的方法之前,先与rpc服务端建立连接 - 使用Call 方法调用远程方法 ### 延伸 其实细心的朋友会注意到client.go 里面有client.Call 和 client.Go 调用; 查看源码可以看到client.Call 底层就是调用的client.Go ``` // 部分源码: / Go invokes the function asynchronously. It returns the Call structure representing // the invocation. The done channel will signal when the call is complete by returning // the same Call object. If done is nil, Go will allocate a new channel. // If non-nil, done must be buffered or Go will deliberately crash. func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { call := new(Call) call.ServiceMethod = serviceMethod call.Args = args call.Reply = reply if done == nil { done = make(chan *Call, 10) // buffered. } else { // If caller passes done != nil, it must arrange that // done has enough buffer for the number of simultaneous // RPCs that will be using that channel. If the channel // is totally unbuffered, it's best not to run at all. if cap(done) == 0 { log.Panic("rpc: done channel is unbuffered") } } call.Done = done client.send(call) return call } // Call invokes the named function, waits for it to complete, and returns its error status. func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error { call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done return call.Error } ``` ### 参考文章 - [gRPC官方文档](https://grpc.io/docs/) - [gRPC中文文档](http://doc.oschina.net/grpc) 如果你对Go也感兴趣, 可以关注我的公众号 ![](https://images.xiaozhuanlan.com/uploads/photo/2019/ae542833-2b23-4c71-b865-7d9ddbc6fcdb.png!large)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

661 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传