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(一般情况下默认会自带)
bash
sudo apt-get install wget

2.更换国内软件源
先备份原来的/etc/apt/source.list 文件
因为我没有下载vim编辑器,所以我先下载:

备份:

这个是我vim前的结果:

添加软件源文件内容:
逐项检查都通过:
| 检查项 | 结果 |
|---|---|
代号 jammy |
全程一致 |
| 清华源 | 换好了 |
deb-src 已注释 |
正确(日常不需要) |
| main / restricted / universe / multiverse | 齐全 |
| updates / backports | 齐全 |
但有一处要改:security 源还是国外的
看你文件最后几行:
代码块
bash
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 里保存退出
代码块
bash
按 Esc
输入 :wq
按回车
第 2 步:一条命令替换
bash
sudo sed -i 's|http://security.ubuntu.com/ubuntu|https://mirrors.tuna.tsinghua.edu.cn/ubuntu|g' /etc/apt/sources.list
确认改对了:
bash
grep security /etc/apt/sources.list

新增完毕后,更新源
bash
sudo apt-get update

3.安装 lrzsz 传输工具

4.安装编译器

5.安装项目构建工具 make

6.安装调试器

7.安装 git


8.安装 cmake


9.安装 Protobuf
- A.安装 Protobuf 依赖库
bash
sudo apt-get install autoconf automake libtool curl unzip gzip
#验证
which unzip gzip autoconf automake libtool curl



- B.下载源码
bash
wget https://gitee.com/qigezi/bitmq/raw/master/mqthird/protobuf-all-3.20.2.tar.gz


- C.编译安装
bash
# 解压缩
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





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

这是经典问题:库装上了,但系统还找不到它
报错含义:
bash
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)
说明库装好了,只是缓存没刷新。跑:
bash
sudo ldconfig
protoc --version

情况 B:无输出 / No such file
说明 sudo make install 没成功。重跑:
bash
cd ~/protobuf-3.20.2
sudo make install
sudo ldconfig
protoc --version
如果 ldconfig 之后还是报同样的错
说明 /usr/local/lib 不在默认搜索路径里,手动加进去:
bash
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
bash
syntax = "proto3";
package contacts;
message PeopleInfo {
string name = 1;
int32 age = 2;
}
第 2 步:编译成 C++ 代码
bash
protoc --cpp_out=./ contacts.proto
protoc:protobuf 的编译器--cpp_out=./:生成 C++ 代码,输出目录是当前文件夹./contacts.proto:你写的协议描述文件执行完这条命令,自动产出 2 个文件:
contacts.pb.h:头文件(类声明)contacts.pb.cc:源文件(类实现)

第 3 步:写 test.cc

bash
#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 步:编译运行
bash
g++ -std=c++11 test.cc contacts.pb.cc -o test -lprotobuf
./test

10.安装 Muduo
bash
下载源码 → 装依赖 → 修复 protorpc → 编译安装 → 验证
- A.下载源码
bash
cd ~/code
git clone https://github.com/chenshuo/muduo.git
GitHub 慢就用备用地址:
bash
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.编译安装
bash
cd muduo-master # 或 muduo
./build.sh
./build.sh install


- E.验证
bash
cd build/release-cpp11/bin
./protobuf_server 9091 # 终端 1
./protobuf_client 0.0.0.0 9091 # 终端 2
看到 Jump! Win! = 成功。
目录保持一致:


11.安装 SQLite3
bash
# 安装sqlite3运行库 + C/C++开发头文件
sudo apt-get install sqlite3 libsqlite3-dev
# 验证安装是否成功
sqlite3 --version


12.安装 Gtest
bash
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
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 做序列化,跑通就说明整个技术栈(网络 + 序列化)已经就位。
