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类型的值
    用代码表示

    1. func (t T*)MethodName(argType T1,replyType *T2)error
  • 在上面这行代码中,类型 T T1 T2 默认会使用Go内置的encoding/gob包进行编码和解码
    改方法的第一个参数表示由PRC客户端传入的参数,第二个参数表示要返回给PRC客户端的结果。改方法最后返回一个error类型

  • RPC客户端和服务器端的使用
    1. RPC服务端可以通过调用 ```rpc.ServerConn```处理单个连接请求。多数情况下,通过tcp或是http在某个网络地址上监听然后再创建该服务是个不错的选择
    2. RPC客户端,Gonet/rpc包提供了便利的```rpc.Dial()``````rpc.DialHTTP()```方法来与指定的RPC服务建立连接。在建立连接之后,Gonet/rpc包允许我们使用通过或者异步的方式接受RPC服务端的结果。调用RPC客户端的```Call()```方法则进行同步处理。这个时候客户端程序按照顺序执行。当调用RPC客户端的```Go()```方法时,则进行异步处理。客户端无需等待服务端的结果即可执行后面的程序,当接收到服务端响应时,再对其进行相应的处理。 无论是哪个方法,都必须要指定要调用的服务及其方法名称,以及一个客户端传入参数的引用,还有一个用于接收处理结果参数的指针
    3. 如果没有指定RPC传输过程中使用何种编码解码器,默认使用Go标准库提供的encoding/gob包进行数据传输

3 代码示例

演示:net/rpc/jsonrpc库

服务器端代码

  1. package main
  2. import (
  3. "log"
  4. "net"
  5. "net/rpc"
  6. "net/rpc/jsonrpc"
  7. )
  8. // 请求对象
  9. type CalculateRequest struct {
  10. Right int
  11. Left int
  12. }
  13. // 响应对象
  14. type CalculateResponse struct {
  15. Result int
  16. }
  17. type Calculator struct {
  18. }
  19. func (c *Calculator) Add(request *CalculateRequest, response *CalculateResponse) error {
  20. log.Printf("[+]call add method\n")
  21. response.Result = request.Right + request.Left
  22. return nil
  23. }
  24. func main() {
  25. addr := ":9999"
  26. // 注册服务,未指定服务名称,默认结构体名
  27. rpc.Register(&Calculator{})
  28. //
  29. rpc.RegisterName("calc", &Calculator{})
  30. listener, err := net.Listen("tcp", addr)
  31. if err != nil {
  32. log.Fatal(err)
  33. }
  34. defer listener.Close()
  35. log.Printf("[+]listen on %s", addr)
  36. for {
  37. conn, err := listener.Accept()
  38. if err != nil {
  39. log.Printf("[-]error client:%s\n", err.Error())
  40. continue
  41. }
  42. log.Printf("[+]client connected:%s\n", conn.RemoteAddr())
  43. //使用例程启动jsonrpc处理客户端请求
  44. go jsonrpc.ServeConn(conn)
  45. }
  46. }

客户端代码

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/rpc/jsonrpc"
  6. )
  7. // 请求对象
  8. type CalculateRequest struct {
  9. Right int
  10. Left int
  11. }
  12. // 响应对象
  13. type CalculateResponse struct {
  14. Result int
  15. }
  16. func main() {
  17. addr := "127.0.0.1:9999"
  18. conn, err := jsonrpc.Dial("tcp", addr)
  19. if err != nil {
  20. log.Fatal(err)
  21. }
  22. defer conn.Close()
  23. //定义请求对象
  24. request := &CalculateRequest{2, 5}
  25. //定义响应对象
  26. response := &CalculateResponse{}
  27. // 调用远程方法
  28. err = conn.Call("calc.Add", request, response)
  29. // 获取结果
  30. fmt.Println(err, response.Result)
  31. }

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