Golang实现RPC通信

在Go语言中实现RPC(Remote Procedure Call)相对简单,因为Go标准库中已经包含了net/rpc包,它提供了创建RPC服务的基础设施。下面是一个简单的RPC服务和客户端的例子

服务端

首先,定义一个服务对象,方法是首字母大写的方法。这些方法需要有两个参数,第一个是接收参数,第二个是返回给客户端的结果参数,此外还需要一个返回值error。

package main

import (
	"fmt"
	"net/http"
	"net/rpc"
)

type Rect struct {
}

type Params struct {
	Height int
	Width  int
}

func (r *Rect) Area(p Params, ret *int) error {
	*ret = p.Width * p.Height
	return nil
}

func main() {
	rect := new(Rect)
	// 注册RPC服务
	rpc.Register(rect)
	rpc.HandleHTTP()
	// 监听TCP端口
	err := http.ListenAndServe(":8889", nil)
	if err != nil {
		fmt.Println("err:", err)
	}
}

客户端

客户端需要使用rpc.Dial来连接RPC服务端,并调用Call方法来执行远程方法。

package main

import (
	"fmt"
	rpc2 "net/rpc"
)

type Params struct {
	Height int
	Width  int
}

func main() {
	rp, err := rpc2.DialHTTP("tcp", "127.0.0.1:8889")
	if err != nil {
		fmt.Println("error1", err)
	}
	ret := 0
	err = rp.Call("Rect.Area", Params{100, 20}, &ret)
	if err != nil {
		fmt.Println("error", err)
	}
	fmt.Println("area", ret)
}

调用过程

  • 客户端调用本地的RPC代理存根(stub),就像调用本地函数一样
  • 存根将调用的方法和传递的参数打包成一种网络可传输的形式(消息),这个过程称为序列化或编组(marshalling)
  • 客户端通过网络将这个打包好的请求信息发送给服务端
  • 服务端的监听器接收到请求,将请求数据传递给服务端的RPC代理存根。
  • 服务端的存根解包(反序列化或解编组)请求信息,获取方法名和参数。(unmarshalling)。
  • 服务端存根根据解包得到的信息,调用本地的服务程序。
  • 服务执行完毕后,服务端存根将结果打包成网络可传输的形式。
  • 服务端将打包后的执行结果通过网络发送回客户端。
  • 客户端的RPC代理存根接收到响应信息。
  • 客户端存根解包响应信息,获取执行结果。
  • 客户端存根将解包后的结果返回给客户端程序,就像本地调用的返回结果一样。