rpc 就是 远程过程调用 指的是调用远端服务器上的程序的方法整个过程。
rpc 理论
RPC技术在架构设计上有四部分组成,分别是:客户端、客户端存根、服务端、服务端存根。
客户端:服务调用发起方 又叫做 服务消费者
服务器: server上运行的可以被客户端调用的方法
客户端存根:存放server端的地址和端口消息 。 将客户端消息打包转换成网络消息 发送给服务端 。 接受服务端的数据包 该程序运行在客户端。
服务端存根:存放client 网络数据 解析数据 调用本地对应的方法运行,将产生的结果回给客户端 该程序运行在 服务端。
go语言自带的rpc 框架的使用
1 服务定义及暴露
在编程实现过程中,服务器端需要注册结构体对象,然后通过对象所属的方法暴露给调用者,从而提供服务,该方法称之为输出方法,此输出方法可以被远程调用。
当然,在定义输出方法时,能够被远程调用的方法需要遵循一定的规则。我们通过代码进行讲解:
func (t *T) MethodName(request T1,response *T2) error
上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是:
- 1、对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。
- 2、方法的第二个参数必须是指针类型。
- 3、方法的返回类型为error。
- 4、方法的类型是可输出的。
- 5、方法本身也是可输出的。
我们举例说明:假设目前我们有一个需求,给出一个float类型变量,作为圆形的半径,要求通过RPC调用,返回对应的圆形面积。具体的编程实现思路如下:
type MathUtil struct{
}
//该方法向外暴露:提供计算圆形面积的服务
func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error { *resp = math.Pi * req * req //圆形的面积 s = π * r * r return nil //返回类型 }
在上述的案例中,我们可以看到:
- 1、Calculate方法是服务对象MathUtil向外提供的服务方法,该方法用于接收传入的圆形半径数据,计算圆形面积并返回。
- 2、第一个参数req代表的是调用者(client)传递提供的参数。
- 3、第二个参数resp代表要返回给调用者的计算结果,必须是指针类型。
- 4、正常情况下,方法的返回值为是error,为nil。如果遇到异常或特殊情况,则error将作为一个字符串返回给调用者,此时,resp参数就不会再返回给调用者。
至此为止,已经实现了服务端的功能定义,接下来就是让服务代码生效,需要将服务进行注册,并启动请求处理。
2 注册服务及监听请求
net/rpc包为我们提供了注册服务和处理请求的一系列方法,结合本案例实现注册及处理逻辑,如下所示:
//1、初始化指针数据类型
mathUtil := new(MathUtil) //初始化指针数据类型 //2、调用net/rpc包的功能将服务对象进行注册 err := rpc.Register(mathUtil) if err != nil { panic(err.Error()) } //3、通过该函数把mathUtil中提供的服务注册到HTTP协议上,方便调用者可以利用http的方式进行数据传递 rpc.HandleHTTP() //4、在特定的端口进行监听 listen, err := net.Listen("tcp", ":8081") if err != nil { panic(err.Error()) } go http.Serve(listen, nil)
经过服务注册和监听处理,RPC调用过程中的服务端实现就已经完成了。接下来需要实现的是客户端请求代码的实现。
3 客户端调用
在服务端是通过Http的端口监听方式等待连接的,因此在客户端就需要通过http连接,首先与服务端实现连接。
-
客户端连接服务端
client, err := rpc.DialHTTP("tcp", "localhost:8081") if err != nil { panic(err.Error()) }
-
远端方法调用 客户端成功连接服务端以后,就可以通过方法调用调用服务端的方法,具体调用方法如下:
var req float32 //请求值 req = 3 var resp *float32 //返回值 err = client.Call("MathUtil.CalculateCircleArea", req, &resp) if err != nil { panic(err.Error()) } fmt.Println(*resp)
上述的调用方法核心在于client.Call方法的调用,该方法有三个参数,
-
第一个参数表示要调用的远端服务的方法名,第二个参数是调用时要传入的参数,第三个参数是调用要接收的返回值。 上述的Call方法调用实现的方式是同步的调用,除此之外,还有一种异步的方式可以实现调用。异步调用代码实现如下:
-
var respSync *float32 //异步的调用方式 syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil) replayDone := <-syncCall.Done fmt.Println(replayDone) fmt.Println(*respSync)
4 多参数的请求调用参数传递
上述内容演示了单个参数下的RPC调用,对于多参数下的请求该如何实现。我们通过另外一个案例进行演示。
假设现在需要实现另外一个需求:通过RPC调用实现计算两个数字相加功能并返回计算结果。此时,就需要传递两个参数,具体实现如下:
将参数定义在一个新的结构体中,存放在param包中:
type AddParma struct {
Args1 float32 //第一个参数 Args2 float32 //第二个参数 }
在server.go文件中,实现两数相加的功能,并实现服务注册的逻辑:
func (mu *MathUtil) Add(param param.AddParma, resp *float32) error { *resp = param.Args1 + param.Args2 //实现两数相加的功能 return nil } mathUtil := new(MathUtil) err := rpc.RegisterName("MathUtil", mathUtil)//多参数的调用 设置方法就在这里调整的 对比下上面的 单个参数的注册区别 if err != nil { panic(err.Error()) } rpc.HandleHTTP() listen, err := net.Listen("tcp", ":8082") http.Serve(listen, nil)
在本案例中,我们通过新的注册方法rpc.RegisterName 实现了服务的注册和调用。
至此,我们已经完成了net/rpc包的最基础的使用。
https://github.com/rubyhan1314/Golang-100-Days/tree/master/Day80(RPC%E8%BF%9C%E7%A8%8B%E8%B0%83%E7%94%A8%E6%9C%BA%E5%88%B6)/code
上面的连接对应三个例子 代码对应三个 一个是单个 参数 一个是多个参数 一个就是prob 封装的参数 环境安装啃爹 prob go语言到环境到 protobuf构建有点麻烦