最近在学MIT6.824的lab1——MapReduce,发现其中使用到了RPC来让work和coordinator之间通信,故而乘机学习一下。
介绍
RPC(Remote Procedure Call,远程过程调用)是一种在分布式系统中进行进程间通信的协议。它允许一个程序(客户端)调用另一个程序(服务器)上的函数或过程,就像调用本地函数一样,而不必关心底层网络细节。
在 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 例子:
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。:
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 和一个用于接收结果的变量。最后,打印出调用结果:
Result of 3 * 4: 12
参考链接
- https://jaminzhang.github.io/architecture/RPC-Introduction/
- https://zhuanlan.zhihu.com/p/187560185
- https://cloud.tencent.com/developer/article/2021745
- ChatGPT