golang之RPC学习
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, ")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, ")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, ")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学习相关推荐
- golang实现RPC的几种方式
golang实现RPC的几种方式 https://studygolang.com/articles/14336 什么是RPC 远程过程调用(Remote Procedure Call,缩写为 RPC) ...
- Golang底层原理学习笔记(一)
LCY~~Golang底层原理学习笔记 1 源码调试 go源代码地址:GitHub - golang/go: The Go programming language 1.1 源码编译 现在的go语言大 ...
- golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)
此文写在golang游戏开发学习笔记-创建一个能自由探索的3D世界之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里我们要创建一个简单的2D游戏场景以及配套的人物,并实现人物运动和碰撞检测功能 ...
- golang游戏开发学习笔记-创建一个能自由探索的3D世界
此文写在golang游戏开发学习笔记-用golang画一个随时间变化颜色的正方形之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里,我们将创建一个非常简单(只有三个方块)但能自由探索的的3D世 ...
- Golang常用库学习
Golang常用库学习 标准库fmt 标准库log 标准库time 标准库strconv 标准库 testing 单元测试 简单测试 单元测试覆盖率统计 表格驱动测试 性能(基准)测试 标准库 os ...
- golang——net/rpc包学习
1.rpc包 rpc包提供了通过网络或其他I/O连接对一个对象的导出方法的访问. 只有满足如下标准的方法才能用于远程访问,其余方法会被忽略: (1)方法是导出的 (2)方法有两个参数,都是导出类型或内 ...
- golang库context学习
context库 context最早的背景说明还是来源于官方的 博客,说明如下: 在Go服务器中,每个传入请求都在其自己的goroutine中进行处理. 请求处理程序通常会启动其他goroutine来 ...
- [Golang]Go语言学习资源集合
说明 对于新手来讲,入门一门新的语言无疑是有困难的,往往会因为找不到方向而迷失.在我的学习golang的过程中,也碰到了该如何入手的问题,还好我善于搜索,有一些基础,入手的时候没有碰到太多困难.但是如 ...
- Golang后端开发学习之路
12月17日 Go语言学习 1.[一文Go起来]快速上手篇 2. IDE:GoLand的安装,破解版 3. golang安装 4.运行第一个go程序 5. 彻底搞懂golang的GOROOT和GOPA ...
最新文章
- 37 函数的定义和调用
- linux内核网络协议栈--linux网络设备理解(十三)
- 洛谷P3391文艺平衡树(Splay)
- how I can force redetermination everytime
- 如何解决数据倾斜问题?
- matlab直方图显示,控制分类直方图的显示
- Docker安装Mysql 案例和Tomcat测试
- 动软代码生成器使用心得
- HDR到底是干什么的?建模的时候有什么用处?
- iOS面试 第三方库
- linux的grub是什么意思,grub是什么意思
- mac移动硬盘安装linux系统安装教程,移动硬盘上安装ubuntu系统
- IDEA使用Maven构建Spring+SpringMVC+MyBatis整合项目demo成功执行但控制台Tomcat Locahost log输出No Spring WebApplicationIn
- 神码ai人工智能写作机器人_机器学习和人工智能最佳书籍
- iMessage推广(群发)技术实现
- 蓝牙协议栈接收数据包流程1
- 从零开始学数据分析之——《线性代数》第四章 线性方程组
- 一个 npm 包的坎坷“续命”之生
- 童鞋想盗取我十几个G的“种子”,看我是用python来层层加锁!!!
- echart 广州3d_echarts绘制3D城市地图