RPC简介

RPC(remote process call)指的是远程过程调用,是分布式系统中不同节点上不同进程直接交流的一种通信形式。传统的函数(方法)调用是在本地直接调用另一个定义好的方法,而RPC则相当于去调用网络上一台服务器上提供的函数。调用时,客户端需要通过网络告诉服务器需要调用哪一个函数,以及该函数的参数。服务器接收并运行函数,结束后将返回结果通过网络发回客户端。

net/rpc包

基础的使用

Go的标准库中也提供了一个简单的RPC实现,可以用于实现网络上Go程序进程之间的rpc通信,缺点是不支持其他语言编写的客户端和服务端。依然可以通过自带的RPC包来学习一些配置rpc通信的基本步骤。

服务端:

  1. 将所有需要暴露的函数配置为一个结构体的方法。
  2. 暴露的方法定义必须满足:
    1. 方法有两个可序列化的参数(第一个代表request, 第二个代表reply必须为指针类型)
    2. 必须返回一个error类型的返回值。
    3. 必须是公开方法(方法名首字母大写)
  3. 通过rpc.RegisterName将一个新的结构体注册为rpc服务,会自动查找这个struct下面所有符合要求的方法,并暴露在rpc服务中,供客户端连接调用。
  4. 通过net.Listen监听一个tcp端口,并将Accept到的conn交给rpc.ServeConn处理。

客户端:

  1. 通过rpc.Dial,传入协议的地址,获得一个client。
  2. 通过client.Call,传入调用的<服务名>.<方法名>和参数,以及一个用于接收结果的地址。调用会自动完成,结果保存在提供的地址对应的变量下面。

更规范的使用

基础使用的案例中,直接在服务端定义对应的rpc服务并在客户端中使用,存在大量依赖的“巧合”。例如,客户端调用中提供的服务名和函数名必须恰巧和服务端提供的服务和函数同名,调用才能正常实现。更为规范和安全的做法是将定义了RPC调用规范的部分从服务端和客户端的代码中抽离解耦,使两边都依赖于一个公共的接口,让巧合成功的调用变为必然。

在接口规范部分需要做的事有:

  1. 定义一个XxxServiceInterface,并要求提供所有需要远程调用的函数签名。这个接口是服务端和客户端使用的client都需要满足的接口。
  2. 定义包装了rpc.Client的服务专用client结构体。为服务专用client结构体添加方法,和远程方法一一对应,供客户端调用使用,避免客户端不清楚有哪些远程方法可以使用。
  3. 编写DialXxxService方法,用于创建一个服务专用的client。供客户端使用,简化客户端中创建client的逻辑。
  4. 编写RegisterXxxService方法,供服务端使用,用于将一个满足XxxServiceInterface的结构体注册为rpc服务,简化服务端的逻辑。
  5. var _ XxxServiceInterface = (*XxxServiceClient)(nil),确保规范中编写的服务专用client接受编译器的检查,满足接口。

更通用的使用

由于Go自带的rpc使用的是自己的协议,并不能在其他语言编写的程序之间使用。使用jsonrpc可以让rpc传输数据时使用json编解码,使Go的rpc数据可以被人和其他程序读取。

服务端只需要将listen得到的connection处理时的rpc.SeveConn(conn)替换为rpc.ServeCodec(jsonrpc.NewServerCodec(conn))。客户端需要通过conn := net.Dial("tcp", "xxx:1234"),并将得到的conn进一步封装:client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))来获得客户端即可。

Protobuf

protobuf本身是一种google提出的消息协议,用于将对象序列化,和JSON属于同样的功能。现在protobuf文件也被用于定义rpc接口,通过自动化的工具可以根据接口生成代码,快速实现接口的功能。

==施工中==