C++ 实现 RPC 分布式网络通信框架

大纲

前言

本文将基于 Muduo 高性能网络库 + Protobuf 开发 RPC 框架,并使用了中间件 ZooKeeper。

开发工具

软件版本说明
C++ 标准17高版本的 Protobuf 库依赖 C++ 17
Boost1.74.0.3Muduo 库依赖 Boost 库
Muduo2.0.3Muduo 库,基于 C++ 开发,用于网络编程
Protobuf31.1Protobuf 库,核心代码主要是用 C++ 开发
G++(GCC)12.2.0建议使用 9 版本的 G++(GCC) 编译器
CMake3.25.1C/C++ 项目构建工具
ZooKeeperZooKeeper 服务器
LinuxDebian 12Muduo 库不支持 Windows 平台
Visual Studio Code1.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 线程池中专门处理耗时的计算任务。
  • (5) reactors in process - one loop pre process

    • 这是 Nginx 服务器的网络设计方案,基于进程设计,采用多个 Reactors 充当 I/O 进程和工作进程,通过一个 accept 锁,完美解决多个 Reactors 之间的 “惊群现象”。

框架介绍

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
      • 封装 epollpoll 的接口(Muduo 默认用 epoll
    • TcpServer / TcpClient
      • 高层封装,简化服务端和客户端的使用
      • 支持多线程连接处理
    • Callback 机制
      • 所有 I/O 事件都通过用户注册的回调函数处理(高扩展性)
  • 性能优势

    • 完全采用非阻塞、异步 I/O 模型
    • 使用智能指针管理资源(如 std::shared_ptr<TcpConnection>
    • 零内存拷贝的数据缓冲机制(Buffer)
    • 合理利用多线程资源(EventLoopThreadPool)
  • 适用场景

    • 高并发 TCP 服务器(如 Redis、MQTT、游戏网关)
    • 微服务通信框架(可自定义通信协议)
    • 高性能 HTTP 服务(支持 HTTP 1.0/1.1)
    • 自研 RPC 系统

平台兼容性

  • Muduo 库只支持 Linux 平台,不兼容 Windows 平台,因为其底层使用了 Linux 平台的 pthreadepoll

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
2
3
4
5
# 安装 Boost 的所有组件和头文件
sudo apt-get install -y libboost-all-dev

# 查看 Bootst 版本
sudo dpkg -s libboost-all-dev | grep Version

提示

由于 Muduo 使用了 Boost 库(如 boost::any),因此需要安装 Boost 库。

安裝 Muduo 库

  • 编译安装 Muduo 库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Git 克隆代码
git clone https://github.com/chenshuo/muduo.git

# 进入代码目录
cd muduo

# 创建构建目录
mkdir -p build

# 进入构建目录
cd build

# 生成构建文件
cmake ..

# 编译源码
make -j$(nproc)

# 执行安装
sudo make install

# 更新系统的共享库缓存
sudo ldconfig /usr/local/lib/
  • 验证安装
1
2
3
4
5
# 查看 Muduo 库的头文件
ls -al /usr/local/include/muduo

# 查看 Muduo 库的静态库
ls -al /usr/local/lib | grep 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.shconfigure 构建系统,转而使用 CMake 作为主要构建系统。
  • Protobuf 从源码编译后,默认只会生成 .a 静态库文件,若希望生成 .so 动态库文件,需要在编译时添加 CMake 参数 -DBUILD_SHARED_LIBS=ON
  • 安装依赖包
1
sudo apt-get -y install cmake g++ make git wget
  • 编译安装 Protobuf 库
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
# 下载源码
wget https://github.com/protocolbuffers/protobuf/archive/refs/tags/v31.1.tar.gz -O protobuf-v31.1.tar.gz

# 解压源码
tar -xvf protobuf-v31.1.tar.gz

# 进入解压目录
cd protobuf-v31.1

# 初始化子模块
git init && git submodule update --init --recursive

# 创建构建目录
mkdir build

# 进入构建目录
cd build

# 生成构建文件(Makefile)
cmake .. -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local

# 编译源码(耗时较长)
make -j2

# 执行安装(包括可执行文件、头文件和库)
sudo make install

# 更新系统的共享库缓存
sudo ldconfig /usr/local/lib/
  • 验证 Protobuf 库安装
1
2
3
4
5
6
7
8
# 查看 Protobuf 库的版本
protoc --version

# 查看 Protobuf 库的头文件
ls -al /usr/local/include/google/protobuf

# 查看 Protobuf 库的动态库
ls -al /usr/local/lib/libproto*

安装 ZooKeeper 中间件

项目开发

项目结构

项目代码

项目调试

  • GDB 调试 C/C++ 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# GDB 调试指定程序
gdb example_rpc_provider

# GDB 设置断点
(gdb) break mprpcconfig.cc:19

# GDB 指定参数运行程序(运行后,会停留在断点处)
(gdb) run -i rpc.conf

# GDB 断点调试 - 运行下一行代码
(gdb) n

# GDB 断点调试 - 查看指定变量的值
(gdb) p src_buf

# GDB 退出调试
(gdb) quit

参考资料