C++ 实现 RPC 分布式网络通信框架
大纲
前言
本文将基于 Muduo 高性能网络库 + Protobuf 开发 RPC 框架,并使用了中间件 ZooKeeper。
开发工具
软件 | 版本 | 说明 |
---|---|---|
C++ 标准 | 17 | 高版本的 Protobuf 库依赖 C++ 17 |
Boost | 1.74.0.3 | Muduo 库依赖 Boost 库 |
Muduo | 2.0.3 | Muduo 库,基于 C++ 开发,用于网络编程 |
Protobuf | 31.1 | Protobuf 库,核心代码主要是用 C++ 开发 |
G++(GCC) | 12.2.0 | 建议使用 9 版本的 G++(GCC) 编译器 |
CMake | 3.25.1 | C/C++ 项目构建工具 |
ZooKeeper | ZooKeeper 服务器 | |
Linux | Debian 12 | Muduo 库不支持 Windows 平台 |
Visual Studio Code | 1.100.2 | 使用 VSCode 远程开发特性 |
平台兼容性说明
由于使用了 Muduo 库,且 Muduo 库仅支持 Linux 平台;因此本文提供的所有 RPC 框架代码支持在 Linux 平台运行,不支持 Windows 平台,默认是基于 Debian 12 进行远程开发。
开源 RPC 框架
大厂开源框架
- gRPC 是一个由 Google 开源、基于 HTTP/2 和 Protocol Buffers 的高性能 RPC 框架,使用 C++ 开发,支持多种语言(如 Go、Java、Python 等)。
- bRPC 是百度开源的高性能、可扩展的 RPC 框架,主要用于服务器间通信,采用 C++ 编写,广泛应用于百度内部大规模分布式系统中。
- phxrpc 是腾讯微信后台团队推出的一个非常简洁小巧的 RPC 框架,编译生成的库只有 450K。
个人开源框架
- tinyrpc - c++ async rpc framework. 14w+ qps.
- rocket - c++ rpc framework, simplified version of tinyrpc.
- rest_rpc - modern c++, simple, easy to use rpc framework.
- rpclib - a modern c++ msgpack-RPC server and client library.
- TarsCpp - c++ language framework rpc source code implementation.
基础概念
集群和分布式
集群
- 概念
- 每一台服务器独立运行一个工程的所有模块。
- 优点
- 部署简单:每台机器部署一样的工程,维护相对简单。
- 容错性强:某台机器宕机,其它机器可以顶上,保证服务连续。
- 扩展方便:增加机器即可横向扩展,提高并发处理能力。
- 负载均衡容易:前端加个负载均衡器即可实现请求均衡分发。
- 缺点
- 资源浪费:每台机器都部署所有模块,某些模块可能资源使用率低。
- 维护更新不灵活:改动一个模块需要重新部署整个工程。
- 扩展粒度粗:不能单独扩展某个压力大的模块(有些模块是 CPU 密集型,有些模块是 I/O 密集型),只能整体扩容。
- 概念
分布式
- 概念
- 一个工程拆分了很多模块,每一个模块独立部署运行在一个服务器主机上,所有服务器协同工作共同提供服务。
- 每一台服务器称作分布式的一个节点,根据节点的并发要求,对一个节点可以再做节点模块集群部署。
- 优点
- 资源利用率高:按模块实际资源需求进行部署,提高系统整体资源使用效率。
- 扩展灵活:哪个模块压力大就单独扩展它,不影响其它模块。
- 服务解耦:模块之间独立部署、独立维护,开发和运维更灵活。
- 技术异构性好:不同模块可以使用不同的技术栈,更自由地选择工具。
- 缺点
- 系统复杂度高:涉及服务间通信、分布式事务、网络延迟等问题。
- 开发运维成本高:部署、调试、监控、容错等方面的技术要求更高。
- 依赖网络稳定性:模块之间通过网络通信,一旦网络出问题可能会引发级联故障。
- 概念
特别注意
在企业的生产环境中,集群和分布式是并存的,两者并不是分开的。
RPC 通信原理
RPC(Remote Procedure Call)远程过程调用的工作流程如下图所示:
- 黄色部分:设计 RPC 方法参数的打包和解析,也就是数据的序列化和反序列化,可以使用 Protobuf 实现。
- 绿色部分:网络通信部分,包括寻找 RPC 服务主机、发起 RPC 调用请求和响应 RPC 调用结果,可以使用 Muduo 网络库和 ZooKeeper(用于服务注册与发现)实现 。
网络 I/O 模型
主流的网络 I/O 模型有以下几种,Muduo 采用的是第四种(reactors in threads - one loop per thread
)。
(1)
accept + read/write
- 不适用于并发服务器
(2)
accept + fork - process-pre-connection
- 适合并发连接数不大,计算任务工作量大于 Fork 的开销。
(3)
accept + thread - thread-pre-connection
- 比第二种网络 I/O 模型的开销小了一点,但是并发造成的线程堆积过多。
(4)
reactors in threads - one loop per thread
- 这是 Muduo 库的网络设计方案,底层实质上是基于 Linux 的
epoll
+pthread
线程池实现,且依赖了 Boost 库,适用于并发连接数较大的场景。 - 有一个 Main Reactor 负载 Accept 连接,然后将连接分发给某个 SubReactor(采用轮询的方式来选择 SubReactor),该连接的所用操作都在那个 SubReactor 所处的线程中完成。多个连接可能被分派到多个线程中被处理,以充分利用 CPU。
- 有一个 Base I/O Thread 负责 Accept 新的连接,接收到新的连接以后,使用轮询的方式在 Reactor Pool 中找到合适的 SubReactor 将这个连接挂载上去,这个连接上的所有任务都在这个 SubReactor 所处的线程中完成。
- Reactor Poll 的大小是固定的,根据 CPU 的核心数量来确定。如果有过多的耗费 CPU 资源的计算任务,可以提交到 ThreadPool 线程池中专门处理耗时的计算任务。
- 这是 Muduo 库的网络设计方案,底层实质上是基于 Linux 的
(5)
reactors in process - one loop pre process
- 这是 Nginx 服务器的网络设计方案,基于进程设计,采用多个 Reactors 充当 I/O 进程和工作进程,通过一个
accept
锁,完美解决多个 Reactors 之间的 “惊群现象”。
- 这是 Nginx 服务器的网络设计方案,基于进程设计,采用多个 Reactors 充当 I/O 进程和工作进程,通过一个
框架介绍
Muduo 的简介
Muduo 是一个用 C++ 编写的高性能、基于事件驱动的网络库,专门设计用于构建 Linux 下高并发、低延迟的网络服务,特别适合开发分布式系统、微服务、消息中间件、网络游戏服务器等后端程序。
核心特性
- 基于事件驱动模型:使用 Reactor 模式,即单线程 I/O + 多线程计算。
- 高性能:使用
epoll
I/O 多路复用技术、非阻塞 I/O、零内存拷贝技术。 - 线程安全:网络部分是线程安全的,使用线程池和回调。
- C++ 11 标准:需要使用支持 C++ 11 的编译器。
- 仅支持 Linux 平台:利用 Linux 特性优化性能,不支持跨平台。
- 可组合性强:解耦的模块设计,便于扩展和组合。
核心模块
base
(基础模块)- 非网络相关的通用工具
- 如线程池、时间戳、日志系统、原子操作等
net
(网络模块)- TCP 服务器 / 客户端模型
- Reactor 事件分发器
- Buffer、Channel、EventLoop、TcpConnection 等核心组件
核心组件
- EventLoop
- 事件循环,是每个线程的核心对象
- 封装了
epoll
库,处理文件描述符的读写事件
- Channel
- 表示一个
fd
(文件描述符)及其感兴趣的事件(如读写) - 是 EventLoop 与具体 I/O 事件之间的桥梁
- 表示一个
- Poller
- 封装
epoll
或poll
的接口(Muduo 默认用epoll
)
- 封装
- TcpServer / TcpClient
- 高层封装,简化服务端和客户端的使用
- 支持多线程连接处理
- Callback 机制
- 所有 I/O 事件都通过用户注册的回调函数处理(高扩展性)
- EventLoop
性能优势
- 完全采用非阻塞、异步 I/O 模型
- 使用智能指针管理资源(如
std::shared_ptr<TcpConnection>
) - 零内存拷贝的数据缓冲机制(Buffer)
- 合理利用多线程资源(EventLoopThreadPool)
适用场景
- 高并发 TCP 服务器(如 Redis、MQTT、游戏网关)
- 微服务通信框架(可自定义通信协议)
- 高性能 HTTP 服务(支持 HTTP 1.0/1.1)
- 自研 RPC 系统
平台兼容性
- Muduo 库只支持 Linux 平台,不兼容 Windows 平台,因为其底层使用了 Linux 平台的
pthread
和epoll
。
Protobuf 的简介
Protocol Buffers(简称 Protobuf)是 Google 提出的一种高效、可扩展的结构化数据序列化格式,用于数据交换。它独立于平台和编程语言,具有良好的跨平台兼容性和扩展性。
Google 为多种主流编程语言提供了 Protobuf 的官方实现,包括 Java、C#、C++、Go 和 Python 等。每种语言的实现都包含相应的编译器插件(
protoc
)和运行时库,使得开发者可以在不同语言间无缝进行数据通信。由于 Protobuf 采用紧凑的二进制编码格式,其序列化和反序列化效率远高于基于文本的格式。相比 XML,Protobuf 的传输效率可提高约 20 倍;相比 JSON,也有近 10 倍的性能提升。这使得它特别适用于对性能要求高的场景。
Protobuf 广泛应用于分布式系统间的数据通信、异构平台的数据交换,也适合用作网络传输协议的数据格式、高效配置文件的载体、或用于数据持久化存储。作为一种兼具效率与可维护性的序列化方案,Protobuf 在大规模系统设计中具有极高的实用价值。
项目介绍
项目结构
项目技术栈
- Protobuf 数据序列化和反序列化协议
- ZooKeeper 分布式一致性协调服务应用以及编程
- Muduo 网络库编程
- Conf 配置文件读取
- 异步日志记录
- CMake 构建项目集成编译环境
准备工作
安装 Boost 库
1 | # 安装 Boost 的所有组件和头文件 |
提示
由于 Muduo 使用了 Boost 库(如 boost::any
),因此需要安装 Boost 库。
安裝 Muduo 库
- 编译安装 Muduo 库
1 | # Git 克隆代码 |
- 验证安装
1 | # 查看 Muduo 库的头文件 |
提示
- Muduo 的编译依赖 CMake 和 Boost 库,默认编译生成的是静态库(
.a
),如果需要编译生成共享库(.so
),可以自行修改CMakeLists.txt
中的配置。 - Muduo 支持 C++ 11,仅支持 Linux 平台,不支持 Windows 平台,建议使用
7.x
及以后版本的g++
编译器。
安装 Protobuf 库
提示
- Protobuf 各个版本的源码包可以从 GitHub Release 下载得到。
- Protobuf 从
3.21
版本开始,Google 官方已经弃用了autogen.sh
和configure
构建系统,转而使用 CMake 作为主要构建系统。 - Protobuf 从源码编译后,默认只会生成
.a
静态库文件,若希望生成.so
动态库文件,需要在编译时添加 CMake 参数-DBUILD_SHARED_LIBS=ON
。
- 安装依赖包
1 | sudo apt-get -y install cmake g++ make git wget |
- 编译安装 Protobuf 库
1 | # 下载源码 |
- 验证 Protobuf 库安装
1 | # 查看 Protobuf 库的版本 |
安装 ZooKeeper 中间件
项目开发
项目结构
项目代码
项目调试
- GDB 调试 C/C++ 代码
1 | # GDB 调试指定程序 |