rpc

什么是rpc

我们知道Socket和HTTP采用的是类似"信息交换"模式,即客户端发送一条信息到服务端,然后(一般来说)服务器端都会返回一定的信息以表示响应。客户端和服务端之间约定了交互信息的格式,以便双方都能够解析交互所产生的信息。但是很多独立的应用并没有采用这种模式,而是采用类似常规的函数调用的方式来完成想要的功能。

RPC就是想实现函数调用模式的网络化。客户端就像调用本地函数一样,然后客户端把这些参数打包之后通过网络传递到服务端,服务端解包到处理过程中执行,然后执行的结果反馈给客户端。

RPC(Remote Procedure Call Protocol)——远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。它假定某些传输协议的存在,如TCP或UDP,以便为通信程序之间携带信息数据。通过它可以使函数调用模式网络化。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

rpc可以做什么

API、进程间通信。主要用于分布式应用间通信

RPC和rest风格api有什么不同?

本质区别就是REST是使用http协议,相比RPC的实现协议传输会传更多的内容,但是两个可以做相同的事情。

RPC工作原理


运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:

  • 1.调用客户端句柄;执行传送参数
    2.调用本地系统内核发送网络消息
    3.消息传送到远程主机
    4.服务器句柄得到消息并取得参数
    5.执行远程过程
    6.执行的过程将结果返回服务器句柄
    7.服务器句柄返回结果,调用远程系统内核
    8.消息传回本地主机
    9.客户句柄由内核接收消息
    10.客户接收句柄返回的数据

go语言中的rpc

Go标准包中已经提供了对RPC的支持,而且支持三个级别的RPC:TCP、HTTP、JSONRPC。但Go的RPC包是独一无二的RPC,它和传统的RPC系统不同,它只支持Go开发的服务器与客户端之间的交互,因为在内部,它们采用了Gob来编码。

Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:

  • 函数必须是导出的(首字母大写)
  • 必须有两个导出类型的参数,
  • 第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数必须是指针类型的
  • 函数还要有一个返回值error

举个例子,正确的RPC函数格式如下:

func (t *T) MethodName(argType T1, replyType *T2) error

T、T1和T2类型必须能被encoding/gob包编解码。

任何的RPC都需要通过网络来传递数据,Go RPC可以利用HTTP和TCP来传递数据,利用HTTP的好处是可以直接复用net/http里面的一些函数。详细的例子请看下面的实现

rpc版的"hello world"

初级

  • 服务端:
package mainimport ("log""net""net/rpc"
)type HelloServer struct {}func (p *HelloServer) Hello(request string, reply *string)  error {*reply = "Hello " + requestreturn nil
}func main(){// 将HelloServer注册为一个RCP服务rpc.RegisterName("HelloServer", new(HelloServer))listener, err := net.Listen("tcp", ":1234")if (err != nil){log.Fatal("ListenTCP error:" , err)}conn, err := listener.Accept()if (err != nil){log.Fatal("Accept error:" , err)}rpc.ServeConn(conn)
}
  • 客户端:
package mainimport ("fmt""log""net/rpc"
)func main(){client, err := rpc.Dial("tcp", ":1234")if (err != nil){log.Fatal("Dialing error:" , err)}var reply stringerr = client.Call("HelloServer.Hello", "hello", &reply)if (err != nil){log.Fatal("Calling error:" , err)}fmt.Println(reply)
}

进化【等待研究】

  • 制定接口规范
package apiimport "net/rpc"const HelloServiceName = "path/to/pck.HelloServer"type HelloServiceInterface = interface {Hello(request string, reply *string) error
}/// 注册
func RegisterHelloServer(svc HelloServiceInterface) error  {return rpc.RegisterName(HelloServiceName, svc)
}type HelloServiceClient struct {*rpc.Client
}var _ HelloServiceInterface = (*HelloServiceClient)(nil) // HelloServiceClient 必须实现HelloServiceInterface方法
func (p *HelloServiceClient) Hello(request string, reply *string) error {return p.Client.Call(HelloServiceName+".Hello", request, reply)
}func DialHelloService(netWork, address string)(*HelloServiceClient, error)  {c, err := rpc.Dial(netWork, address)if(err != nil){return nil, err}return &HelloServiceClient{Client:c}, nil
}
  • 客户端:
package mainimport ("fmt""log""test/api"
)func main(){// 拨号client, err := api.DialHelloService("tcp", ":1234")if (err != nil){log.Fatal("Dialing error:" , err)}var reply stringerr = client.Hello("hello", &reply)if (err != nil){log.Fatal("Calling error:" , err)}fmt.Println(reply)
}
  • 服务端:
package mainimport ("log""net""net/rpc""test/api"
)type HelloServer struct {}func (p *HelloServer) Hello(request string, reply *string)  error {*reply = "Hello " + requestreturn nil
}func main(){// 将HelloServer注册为一个RCP服务api.RegisterHelloServer(new(HelloServer))listener, err := net.Listen("tcp", ":1234")if (err != nil){log.Fatal("ListenTCP error:" , err)}for{conn, err := listener.Accept()if (err != nil){log.Fatal("Accept error:" , err)}go rpc.ServeConn(conn)}
}

例子2:Http RPC

  • 服务端:
package mainimport ("errors""fmt""log""net/http""net/rpc"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}type Arith int// Arith.Multiply
func (t *Arith) Multiply(args *Args, reply *int) error {*reply = args.A * args.Bfmt.Println(reply,"乘法执行了")return nil
}
//Arith.Divide
func (t *Arith) Divide(args *Args, quo *Quotient) error {if args.B == 0 {return errors.New("divide by zero")}quo.Quo = args.A / args.Bquo.Rem = args.A % args.Bfmt.Println(quo,"除法执行了")return nil
}func main(){rpc.Register(new(Arith)) //注册一个Arith的RPC服rpc.HandleHTTP()  // 把该服务注册到了HTTP协议, 然后我们就可以利用http的方式来传递数据了/*err := http.ListenAndServe(":1234", nil)if err != nil {log.Fatal(err.Error())}*/lister,err:=net.Listen("tcp","127.0.0.1:1234")if err != nil {log.Fatal(err.Error())}http.Serve(lister,nil)
}
  • 客户端:
package mainimport ("fmt""log""net/rpc"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}func main(){client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234")if err!=nil{log.Fatal("dialing:", err)}args := Args{17, 8}var reply interr = 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)var quot Quotienterr = client.Call("Arith.Divide", args, &quot)if err != nil {log.Fatal("arith error:", err)}fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)}

例子3:tcp RPC

  • 服务端
package mainimport ("errors""fmt""net""net/rpc""os"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}type Arith intfunc (t *Arith) Multiply(args *Args, reply *int) error {*reply = args.A * args.Breturn 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.Bquo.Rem = args.A % args.Breturn nil
}func main() {arith := new(Arith)rpc.Register(arith)tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")checkError(err)listener, err := net.ListenTCP("tcp", tcpAddr)checkError(err)for {conn, err := listener.Accept()if err != nil {continue}go rpc.ServeConn(conn)}}func checkError(err error) {if err != nil {fmt.Println("Fatal error ", err.Error())os.Exit(1)}
}
  • 客户端:
package mainimport ("fmt""log""net/rpc"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}func main(){//client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234")   --- http fangshi// client, err := jsonrpc.Dial("tcp", service)  ----  JSON RPCclient, err := rpc.Dial("tcp", "127.0.0.1:1234")  //tcp方式if err!=nil{log.Fatal("dialing:", err)}args := Args{17, 8}var reply interr = 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)var quot Quotienterr = client.Call("Arith.Divide", args, &quot)if err != nil {log.Fatal("arith error:", err)}fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)}

例子4:json RPC

package mainimport ("errors""fmt""net""net/rpc""net/rpc/jsonrpc""os"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}type Arith intfunc (t *Arith) Multiply(args *Args, reply *int) error {*reply = args.A * args.Breturn 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.Bquo.Rem = args.A % args.Breturn nil
}func main() {arith := new(Arith)rpc.Register(arith)tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")checkError(err)listener, err := net.ListenTCP("tcp", tcpAddr)checkError(err)for {conn, err := listener.Accept()if err != nil {continue}go jsonrpc.ServeConn(conn)}}func checkError(err error) {if err != nil {fmt.Println("Fatal error ", err.Error())os.Exit(1)}
}
package mainimport ("fmt""log""net/rpc/jsonrpc"
)type Args struct {A, B int
}type Quotient struct {Quo, Rem int
}func main(){//client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234")   --- http fangshiclient, err := jsonrpc.Dial("tcp", "127.0.0.1:1234") // ----  JSON RPC//client, err := rpc.Dial("tcp", "127.0.0.1:1234")  //tcp方式if err!=nil{log.Fatal("dialing:", err)}args := Args{17, 8}var reply interr = 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)var quot Quotienterr = client.Call("Arith.Divide", args, &quot)if err != nil {log.Fatal("arith error:", err)}fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)}

golang之RPC学习相关推荐

  1. golang实现RPC的几种方式

    golang实现RPC的几种方式 https://studygolang.com/articles/14336 什么是RPC 远程过程调用(Remote Procedure Call,缩写为 RPC) ...

  2. Golang底层原理学习笔记(一)

    LCY~~Golang底层原理学习笔记 1 源码调试 go源代码地址:GitHub - golang/go: The Go programming language 1.1 源码编译 现在的go语言大 ...

  3. golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)

    此文写在golang游戏开发学习笔记-创建一个能自由探索的3D世界之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里我们要创建一个简单的2D游戏场景以及配套的人物,并实现人物运动和碰撞检测功能 ...

  4. golang游戏开发学习笔记-创建一个能自由探索的3D世界

    此文写在golang游戏开发学习笔记-用golang画一个随时间变化颜色的正方形之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里,我们将创建一个非常简单(只有三个方块)但能自由探索的的3D世 ...

  5. Golang常用库学习

    Golang常用库学习 标准库fmt 标准库log 标准库time 标准库strconv 标准库 testing 单元测试 简单测试 单元测试覆盖率统计 表格驱动测试 性能(基准)测试 标准库 os ...

  6. golang——net/rpc包学习

    1.rpc包 rpc包提供了通过网络或其他I/O连接对一个对象的导出方法的访问. 只有满足如下标准的方法才能用于远程访问,其余方法会被忽略: (1)方法是导出的 (2)方法有两个参数,都是导出类型或内 ...

  7. golang库context学习

    context库 context最早的背景说明还是来源于官方的 博客,说明如下: 在Go服务器中,每个传入请求都在其自己的goroutine中进行处理. 请求处理程序通常会启动其他goroutine来 ...

  8. [Golang]Go语言学习资源集合

    说明 对于新手来讲,入门一门新的语言无疑是有困难的,往往会因为找不到方向而迷失.在我的学习golang的过程中,也碰到了该如何入手的问题,还好我善于搜索,有一些基础,入手的时候没有碰到太多困难.但是如 ...

  9. Golang后端开发学习之路

    12月17日 Go语言学习 1.[一文Go起来]快速上手篇 2. IDE:GoLand的安装,破解版 3. golang安装 4.运行第一个go程序 5. 彻底搞懂golang的GOROOT和GOPA ...

最新文章

  1. 37 函数的定义和调用
  2. linux内核网络协议栈--linux网络设备理解(十三)
  3. 洛谷P3391文艺平衡树(Splay)
  4. how I can force redetermination everytime
  5. 如何解决数据倾斜问题?
  6. matlab直方图显示,控制分类直方图的显示
  7. Docker安装Mysql 案例和Tomcat测试
  8. 动软代码生成器使用心得
  9. HDR到底是干什么的?建模的时候有什么用处?
  10. iOS面试 第三方库
  11. linux的grub是什么意思,grub是什么意思
  12. mac移动硬盘安装linux系统安装教程,移动硬盘上安装ubuntu系统
  13. IDEA使用Maven构建Spring+SpringMVC+MyBatis整合项目demo成功执行但控制台Tomcat Locahost log输出No Spring WebApplicationIn
  14. 神码ai人工智能写作机器人_机器学习和人工智能最佳书籍
  15. iMessage推广(群发)技术实现
  16. 蓝牙协议栈接收数据包流程1
  17. 从零开始学数据分析之——《线性代数》第四章 线性方程组
  18. 一个 npm 包的坎坷“续命”之生
  19. 童鞋想盗取我十几个G的“种子”,看我是用python来层层加锁!!!
  20. echart 广州3d_echarts绘制3D城市地图

热门文章

  1. 年末了,是该写点ww纪念acm…
  2. 爱奇艺阅读怎么设置字体
  3. mysql5.7如何启动服务_Mysql 5.7.18安装方法及启动MySQL服务的过程详解
  4. 魅族root教程搜索感悟之——直接回答问题
  5. 程序员接私活的必备10个网站
  6. HTML字体 font和链接
  7. CAD怎么转换成PDF?如何将CAD批量转换成PDF?
  8. oppor9s计算机屏幕颜色,OPPO R9
  9. MATLAB三角消元法,matlab中用的高斯消元法怎么使用!
  10. 骑马与砍杀:战团 for Mac(角色扮演类游戏)支持m1