RPC介绍

最近在学MIT6.824的lab1——MapReduce,发现其中使用到了RPC来让work和coordinator之间通信,故而乘机学习一下。

介绍

RPC(Remote Procedure Call,远程过程调用)是一种在分布式系统中进行进程间通信的协议。它允许一个程序(客户端)调用另一个程序(服务器)上的函数或过程,就像调用本地函数一样,而不必关心底层网络细节。

在 RPC 中,客户端和服务器可以在不同的机器上,甚至在不同的网络上。RPC 提供了一种抽象,使得远程调用看起来就像是本地调用一样。通过 RPC,程序可以通过网络传输数据和调用远程函数,使得分布式系统中的组件可以协同工作。

其整体流程如下图所示:

RPC流程

对于RPC需要关注的主要有三点:

  • 通信协议: RPC可以基于TCP或者HTTP协议,一般而言TCP的协议更快
  • 寻址: 远程提供服务器需要提供服务所在地址,例如IP和端口
  • 数据序列化: 远程调用无法依据内存进行参数和结果传递,所以需要规定序列化的格式,例如Json格式

常用的RPC框架有如下这些:

  • Thrift:thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。
  • gRPC:一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
  • Dubbo:Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,Dubbo自2011年开源后,已被许多非阿里系公司使用。
  • Spring Cloud:Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。Spring Cloud基于Spring Boot, 使得开发部署极其简单。

GO语言示例

在 Go 语言中,标准库提供了一个 net/rpc 包,用于实现 RPC。基本的使用流程包括注册对象、注册服务、处理请求等。以下是一个简单的 Go RPC 例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"net"
"net/rpc"
)

// 定义一个用于 RPC 的对象
type MyService struct{}

// 定义一个 RPC 方法
func (s *MyService) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}

// 定义传递给 RPC 方法的参数结构
type Args struct {
A, B int
}

func main() {
// 注册服务
rpc.Register(new(MyService))

// 创建监听器
listener, err := net.Listen("tcp", ":1234")
if err != nil {
panic(err)
}

// 处理连接
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go rpc.ServeConn(conn)
}
}

在该例子中,MyService 结构体中的 Multiply 方法被注册为 RPC 服务。通过监听端口 1234,该服务可以接收客户端的 RPC 调用请求。客户端可以通过 net/rpc 包中的函数来发起远程调用。RPC 在分布式系统中广泛用于实现不同节点之间的通信和协作。

以下是一个简单的 Go RPC 客户端的调用代码示例,程序需要确保 RPC 客户端的网络协议和端口与服务器端一致,这样它们才能正确地进行通信。在这个例子中,服务器端监听的是 TCP 端口 1234。:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"net/rpc"
)

// 定义传递给 RPC 方法的参数结构
type Args struct {
A, B int
}

func main() {
// 连接 RPC 服务器
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
panic(err)
}
defer client.Close()

// 准备 RPC 调用的参数
args := Args{3, 4}

// 调用 Multiply 方法
var result int
err = client.Call("MyService.Multiply", args, &result)
if err != nil {
panic(err)
}

// 打印结果
fmt.Printf("Result of 3 * 4: %d\n", result)
}

在这个例子中,rpc.Dial 函数用于连接到服务器,然后通过 client.Call 方法调用远程的 Multiply 方法。在调用过程中,需要传递参数结构 Args 和一个用于接收结果的变量。最后,打印出调用结果:

1
Result of 3 * 4: 12

参考链接


RPC介绍
http://example.com/2023/11/14/RPC介绍/
作者
滑滑蛋
发布于
2023年11月14日
许可协议