RPC编程
1 介绍
RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。RPC协议构建于TCP或UDP,或者是HTTP上。允许开发者直接调用另一台服务器上的程序,而开发者无需另外的为这个调用过程编写网络通信相关代码,使得开发网络分布式程序在内的应用程序更加容易
RPC采用客户端-服务器端的工作模式,请求程序就是一个客户端,而服务提供程序就是一个服务器端。当执行一个远程过程调用时,客户端程序首先先发送一个带有参数的调用信息到服务端,然后等待服务端响应。在服务端,服务进程保持睡眠状态直到客户端的调用信息到达。当一个调用信息到达时,服务端获得进程参数,计算出结果,并向客户端发送应答信息。然后等待下一个调用。
用通俗易懂的语言描述就是:RPC允许跨机器、跨语言调用计算机程序方法。打个比方,我用go语言写了个获取用户信息的方法getUserInfo,并把go程序部署在阿里云服务器上面,现在我有一个部署在腾讯云上面的php项目,需要调用golang的getUserInfo方法获取用户信息,php跨机器调用go方法的过程就是RPC调用。
2 golang中如何实现RPC
在golang中实现RPC非常简单,有封装好的官方库和一些第三方库提供支持。Go RPC可以利用tcp或http来传递数据,可以对要传递的数据使用多种类型的编解码方式。golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp或http数据传输方式,由于其他语言不支持gob编解码方式,所以使用net/rpc库实现的RPC方法没办法进行跨语言调用。
golang官方还提供了net/rpc/jsonrpc库实现RPC方法,JSON RPC采用JSON进行数据编解码,因而支持跨语言调用。但目前的jsonrpc库是基于tcp协议实现的,暂时不支持使用http进行数据传输。
除了golang官方提供的rpc库,还有许多第三方库为在golang中实现RPC提供支持,大部分第三方rpc库的实现都是使用protobuf进行数据编解码,根据protobuf声明文件自动生成rpc方法定义与服务注册代码,在golang中可以很方便的进行rpc服务调用。
一个对象中只有满足如下条件的方法,才能被PRC服务端设置为可供远程访问
1.必须是在对象外部可公开调用的方法(首字母大写)
2.必须有两个参数,且参数的类型都必须是包外部可以访问的类型或者是Go內建支持的类型
3.第二个参数必须是一个指针
4.方法必须返回一个error类型的值
用代码表示func (t T*)MethodName(argType T1,replyType *T2)error
在上面这行代码中,类型 T T1 T2 默认会使用Go内置的encoding/gob包进行编码和解码
改方法的第一个参数表示由PRC客户端传入的参数,第二个参数表示要返回给PRC客户端的结果。改方法最后返回一个error类型- RPC客户端和服务器端的使用
RPC服务端可以通过调用 ```rpc.ServerConn```处理单个连接请求。多数情况下,通过tcp或是http在某个网络地址上监听然后再创建该服务是个不错的选择
在RPC客户端,Go的net/rpc包提供了便利的```rpc.Dial()```和```rpc.DialHTTP()```方法来与指定的RPC服务建立连接。在建立连接之后,Go的net/rpc包允许我们使用通过或者异步的方式接受RPC服务端的结果。调用RPC客户端的```Call()```方法则进行同步处理。这个时候客户端程序按照顺序执行。当调用RPC客户端的```Go()```方法时,则进行异步处理。客户端无需等待服务端的结果即可执行后面的程序,当接收到服务端响应时,再对其进行相应的处理。 无论是哪个方法,都必须要指定要调用的服务及其方法名称,以及一个客户端传入参数的引用,还有一个用于接收处理结果参数的指针
如果没有指定RPC传输过程中使用何种编码解码器,默认使用Go标准库提供的encoding/gob包进行数据传输
3 代码示例
服务器端代码
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
// 请求对象
type CalculateRequest struct {
Right int
Left int
}
// 响应对象
type CalculateResponse struct {
Result int
}
type Calculator struct {
}
func (c *Calculator) Add(request *CalculateRequest, response *CalculateResponse) error {
log.Printf("[+]call add method\n")
response.Result = request.Right + request.Left
return nil
}
func main() {
addr := ":9999"
// 注册服务,未指定服务名称,默认结构体名
rpc.Register(&Calculator{})
//
rpc.RegisterName("calc", &Calculator{})
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
log.Printf("[+]listen on %s", addr)
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("[-]error client:%s\n", err.Error())
continue
}
log.Printf("[+]client connected:%s\n", conn.RemoteAddr())
//使用例程启动jsonrpc处理客户端请求
go jsonrpc.ServeConn(conn)
}
}
客户端代码
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
// 请求对象
type CalculateRequest struct {
Right int
Left int
}
// 响应对象
type CalculateResponse struct {
Result int
}
func main() {
addr := "127.0.0.1:9999"
conn, err := jsonrpc.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
//定义请求对象
request := &CalculateRequest{2, 5}
//定义响应对象
response := &CalculateResponse{}
// 调用远程方法
err = conn.Call("calc.Add", request, response)
// 获取结果
fmt.Println(err, response.Result)
}
gRPC编程
1 介绍
gRPC中文文档> gRPC是一个高性能、开源、通用的RPC框架。基于HTTP/2协议标准设计开发,默认采用Protocol Buffers数据序列化协议(Protocol Buffers基本语法),支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库
2 gRPC应用场景
在gRPC客户端可以直接调用不通服务器上的远程程序,就想调用本地程序一样,很容易构建分布式应用和服务。和很多RPC系统一样,服务负责实现定义好的接口并处理客户端请求,客户端根据接口描述直接调用需要的服务。客户端和服务器可以分别使用gRPC支持的不同语言实现
参考:
https://studygolang.com/articles/14750
https://studygolang.com/articles/14336