欢迎光临
我们一直在努力

【C++标准项目】C++ 仿 RabbitMQ 实现消息队列(一):项目概述

1. 项目介绍

我们之前学过阻塞队列(BlockingQueue),它最典型的应用场景就是实现生产者消费者模型。生产者消费者模型是后端开发中非常常用的编程方式,它带来诸多好处:

  • 解耦 —— 生产者与消费者互不依赖
  • 支持并发 —— 生产和消费可以并行推进
  • 支持忙闲不均 —— 双方处理速率不一致时,队列充当缓冲区
  • 削峰填谷 —— 突发流量先入队,后端按自身能力匀速消费

在实际的后端开发中,尤其是分布式系统里,跨主机使用生产者消费者模型也是非常普遍的需求。为此,我们通常会把队列封装成一个独立的服务程序,并赋予它更丰富的功能 —— 这类服务程序就称为消息队列(Message Queue,MQ)。

与进程内的阻塞队列相比,MQ 额外具备网络通信、消息持久化和消息路由能力。

市面上成熟的消息队列非常多:

  • RabbitMQ
  • Kafka
  • RocketMQ
  • ActiveMQ

其中 RabbitMQ 知名度高、功能强大、使用广泛。本项目就仿照 RabbitMQ,模拟实现一个简单的消息队列。


2. 开发环境

  • 操作系统:Linux(CentOS 7.6 / Ubuntu 22.04)
  • 编辑器:VSCode / Vim
  • 编译调试:g++ / gdb
  • 构建工具:Makefile

3. 技术选型

  • 开发主语言:C++
  • 序列化框架:Protobuf 二进制序列化
  • 网络通信:自定义应用层协议
    • 原生 socket:可控性最强,但需要自行处理 IO 多路复用、缓冲区管理等细节,复杂度较高
    • muduo 库:封装了 TCP 长连接,采用 epoll 事件驱动模式,能更方便地实现高并发服务器与客户端
  • 数据存储:SQLite3
  • 单元测试框架:GTest

4.环境搭建

Ubuntu22.04 环境搭建

1.安装 wget(一般情况下默认会自带)

sudo apt-get install wget


2.更换国内软件源

先备份原来的/etc/apt/source.list 文件

因为我没有下载vim编辑器,所以我先下载:

备份:

这个是我vim前的结果:

添加软件源文件内容:

逐项检查都通过:

检查项结果
代号 jammy  全程一致
清华源  换好了
deb-src 已注释  正确(日常不需要)
main / restricted / universe / multiverse  齐全
updates / backports  齐全

但有一处要改:security 源还是国外的

看你文件最后几行:

代码块

deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted
deb http://security.ubuntu.com/ubuntu/ jammy-security universe
deb http://security.ubuntu.com/ubuntu/ jammy-security multiverse

security.ubuntu.com 是 Ubuntu 官方安全更新源,服务器在国外,国内访问同样很慢。清华源是有 jammy-security 镜像的,应该一起换掉。

改法(两步)

第 1 步:先在 vim 里保存退出

代码块

按 Esc
输入 :wq
按回车

第 2 步:一条命令替换

sudo sed -i 's|http://security.ubuntu.com/ubuntu|https://mirrors.tuna.tsinghua.edu.cn/ubuntu|g' /etc/apt/sources.list

确认改对了:

grep security /etc/apt/sources.list

新增完毕后,更新源

sudo apt-get update


3.安装 lrzsz 传输工具


4.安装编译器


5.安装项目构建工具 make


6.安装调试器


7.安装 git


8.安装 cmake


9.安装 Protobuf

  • A.安装 Protobuf 依赖库

sudo apt-get install autoconf automake libtool curl unzip gzip
#验证
which unzip gzip autoconf automake libtool curl


  • B.下载源码

wget https://gitee.com/qigezi/bitmq/raw/master/mqthird/protobuf-all-3.20.2.tar.gz


  • C.编译安装

# 解压缩
tar -zxf protobuf-all-3.20.2.tar.gz

# 切换目录
cd protobuf-3.20.2/

# 如果下载的是具体的某一门语言,不需要执行这一步
./autogen.sh

# 配置安装路径 默认安装在 /usr/local 目录
./configure

# 开始编译 15 分钟左右
make

# 开始安装, 需要 root 权限
sudo make install

# 确认是否安装成功
protoc –version
# 期望输出:libprotoc 3.20.2

当我在查看版本的时候它这里报错了:

这是经典问题:库装上了,但系统还找不到它

报错含义:

protoc: error while loading shared libraries: libprotoc.so.31: cannot open shared object file

protoc 是可执行文件,但它运行时还要加载动态链接库 libprotoc.so.31。这两个东西安装后是分开放的:

文件位置
protoc(可执行文件) /usr/local/bin/protoc
libprotoc.so.31(动态库) /usr/local/lib/libprotoc.so.31

系统运行时要去"动态库搜索路径"里找 .so 文件。ldconfig 就是用来重建这个搜索缓存的。

先诊断:库到底装上没

情况 A:有输出(能看到 libprotoc.so.31)

说明库装好了,只是缓存没刷新。跑:

sudo ldconfig
protoc –version

情况 B:无输出 / No such file

说明 sudo make install 没成功。重跑:

cd ~/protobuf-3.20.2
sudo make install
sudo ldconfig
protoc –version


如果 ldconfig 之后还是报同样的错

说明 /usr/local/lib 不在默认搜索路径里,手动加进去:

echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/protobuf.conf
sudo ldconfig
protoc –version

这三条就等价于教程里说的「修改 /etc/ld.so.conf,添加 /usr/local/lib/」,只是更规范的做法是单独写一个 conf 文件,而不是直接改主配置文件。


  • 若安装后的库存放于 /usr/local/lib 中, 导致运行程序时默认检索不到,则可以修改/etc/ld.so.conf 文件,添加库文件路径到其中

  • 修改完毕后,加载配置文件

ldconfig:读取清单,生成缓存,让系统立刻识别新库路径


  • D.测试验证

第 1 步:写 contacts.proto

syntax = "proto3";
package contacts;

message PeopleInfo {
string name = 1;
int32 age = 2;
}


第 2 步:编译成 C++ 代码

protoc –cpp_out=./ contacts.proto

  • protoc:protobuf 的编译器
  • –cpp_out=./:生成 C++ 代码,输出目录是当前文件夹 ./
  • contacts.proto:你写的协议描述文件

执行完这条命令,自动产出 2 个文件:

  • contacts.pb.h:头文件(类声明)
  • contacts.pb.cc:源文件(类实现)

  • 第 3 步:写 test.cc

    #include <iostream>
    #include "contacts.pb.h" // 引入编译生成的头文件
    using namespace std;

    int main() {
    string people_str;

    // 序列化
    {
    // .proto 文件声明的 package,通过 protoc 编译后,
    // 会为编译生成的 C++ 代码声明同名的命名空间
    contacts::PeopleInfo people;
    people.set_age(20);
    people.set_name("张珊");
    // 调用序列化方法,将序列化后的二进制序列存入 string 中
    if (!people.SerializeToString(&people_str)) {
    cout << "序列化联系人失败." << endl;
    }
    // 打印序列化结果
    cout << "序列化后的 people_str: " << people_str.size() << endl;
    }

    // 反序列化
    {
    contacts::PeopleInfo people;
    // 调用反序列化方法,读取 string 中存放的二进制序列,并反序列化出对象
    if (!people.ParseFromString(people_str)) {
    cout << "反序列化出联系人失败." << endl;
    }
    // 打印结果
    cout << "Parse age: " << people.age() << endl;
    cout << "Parse name: " << people.name() << endl;
    }
    return 0;
    }


    第 4 步:编译运行

    g++ -std=c++11 test.cc contacts.pb.cc -o test -lprotobuf
    ./test


    10.安装 Muduo

    下载源码 → 装依赖 → 修复 protorpc → 编译安装 → 验证

    • A.下载源码

    cd ~/code
    git clone https://github.com/chenshuo/muduo.git

    GitHub 慢就用备用地址:

    cd ~/code
    wget https://gitee.com/hansionz/mq/raw/master/resource/muduo-master.zip
    unzip muduo-master.zip


    • B. 安装依赖环境

    sudo apt install -y libz-dev libboost-all-dev


    • C.修复 protorpc

    muduo/net/protorpc/CMakeLists.txt

    这一步是"条件步骤"——只有 D 编译报错了才需要改。


    • D.编译安装

    cd muduo-master # 或 muduo
    ./build.sh
    ./build.sh install


    • E.验证

    cd build/release-cpp11/bin
    ./protobuf_server 9091 # 终端 1
    ./protobuf_client 0.0.0.0 9091 # 终端 2

    看到 Jump! Win! = 成功。

    目录保持一致:


    11.安装 SQLite3

    # 安装sqlite3运行库 + C/C++开发头文件
    sudo apt-get install sqlite3 libsqlite3-dev

    # 验证安装是否成功
    sqlite3 –version


    12.安装 Gtest

    sudo apt-get install libgtest-dev

    测试 GTest 是否安装成功

    • 编写测试代码

    • 编译源文件以及运行可执行文件


    5.参考文档

    a.AMQP 协议

    • AMQP Part I AMQP详解-CSDN博客

      消息队列协议入门,理解 RabbitMQ 的 Exchange / Queue / Binding 模型。


    b.ProtoBuf 的使用与原理

    • protobuf 反射机制 protobuf的反射机制 – sweychen – 博客园 ProtoBuf-反射原理与使用_proto反射-CSDN博客

      不依赖具体消息类型操作字段,消息分发模块会用到。


    c.GTest

    • https://cloud.tencent.com/developer/article/2159465

    • gtest 三种事件机制_gtest teardown-CSDN博客

      单元测试框架使用指南。


    d.muduo

    • muduo::CountDownLatch CountDownLatch的理解和使用 – Shane_Li – 博客园

      多线程同步原语,需要等待多个线程就绪时使用。


    6.小结

    本篇完成了消息队列项目的全部准备工作:从项目定位、技术选型,到一整套开发环境落地。

    环境搭建清单

    组件版本安装方式
    1 wget 系统自带 —
    2 软件源 清华镜像(jammy) 手动配置
    3 lrzsz — apt
    4 gcc / g++ 11.4.0 apt
    5 make 4.x apt
    6 gdb 12.1 apt
    7 git 2.34.1 apt
    8 cmake 3.22.1 apt
    9 Protobuf 3.20.2 源码编译
    10 Muduo master 源码编译
    11 SQLite3 apt 版本 apt
    12 GTest apt 版本 apt

    三个值得记住的点

  • 软件源必须匹配系统代号(22.04 = jammy)。网上大量教程是 20.04 时代写的,照抄 focal 会把系统搞坏。
  • 云服务器不用换源。腾讯云 / 阿里云出厂就配好了内网镜像,比公网源更快,还不计流量。
  • Protobuf 和 Muduo 必须源码编译。apt 源里的版本不匹配,且 muduo 根本没有 apt 包。

  • 验证结果

    检查项命令预期
    Protobuf protoc –version libprotoc 3.20.2
    Muduo + Protobuf 跑 protobuf_server / protobuf_client 客户端打印 Jump! Win!
    SQLite3 sqlite3 –version 输出版本号
    GTest ./gtest [ PASSED ] 1 test.

    Muduo 那一步最有价值——它用 muduo 做网络、protobuf 做序列化,跑通就说明整个技术栈(网络 + 序列化)已经就位。

    赞(0)
    未经允许不得转载:171主机测评 » 【C++标准项目】C++ 仿 RabbitMQ 实现消息队列(一):项目概述
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址