一 概述
本文主要是基于RabbitMQ消息队列,实现一个消息队列中间件动态库.该动态库包括的功能有:
1.连接RabbitMQ服务;
2.设置发送队列参数;
3.设置接收队列参数;
4.发送单播消息到指定队列;
5.发送广播消息到所有队列;
6.开启消费队列;
7.断开与RabbitMQ服务连接.
二 封装接口
cpp
#ifndef IMSMESSAGEQUEUE_H
#define IMSMESSAGEQUEUE_H
#include <QtCore/qglobal.h>
#include <memory>
#include <functional>
using namespace std;
using MessageCallback = std::function<void(const std::string&)>;
#if defined(IMSMESSAGEQUEUE_LIBRARY)
# define IMSMESSAGEQUEUE_EXPORT Q_DECL_EXPORT
#else
# define IMSMESSAGEQUEUE_EXPORT Q_DECL_IMPORT
#endif
class IMessageQueue;
class IMSMESSAGEQUEUE_EXPORT IMSMessageQueue
{
public:
IMSMessageQueue();
/**
* @brief Connect 连接服务器
* @param host
* @param port
* @param username
* @param password
* @param vhost
* @return
*/
bool Connect(const std::string& host, int port,
const std::string& username,
const std::string& password,
const std::string &vhost);
/**
* @brief DisConnect 断开连接
*/
void DisConnect();
/**
* @brief setSendParams 设置发送参数
* @param queueName 发送队列名称
* @param durable true-消息持久化 false-消息非持久化
*/
void SetSendParams(const std::string &queueName,const string exchangeName="", bool durable=true);
/**
* @brief setRecvParams 设置接收参数
* @param queueName 接收队列名称
* @param callback 接收消息回调
* @param exchangeName 交换机名称
* @param durable true-消息持久化 false-消息非持久化
*/
void SetRecvParams(const std::string& queueName,
MessageCallback callback,
const string exchangeName="",
bool durable=true);
/**
* @brief Send 发送消息到服务器指定队列
* @param message
* @return
*/
bool Send(const std::string& message);
/**
* @brief SendBroadcast 发送广播消息
* @param exchange 交换机名称
* @param message 消息
* @return
*/
bool SendBroadcast(const std::string&exchange,const std::string& message);
/**
* @brief StartConsuming 开启消费队列
*/
void StartConsuming();
private:
IMSMessageQueue(const IMSMessageQueue &) = delete;
IMSMessageQueue &operator=(const IMSMessageQueue &) = delete;
private:
std::unique_ptr<IMessageQueue> _mq;
};
#endif // IMSMESSAGEQUEUE_H
三 关键接口实现
1.连接RabbitMQ服务接口实现
cpp
bool RabbitMQAdapter::connect(const std::string &host,
int port,
const std::string &username,
const std::string &password,
const std::string &vhost)
{
std::string connectParam;
connectParam = "amqp://"+username+":"+password+"@"+host+":"+to_string(port)+vhost;
if(_consumer){
_consumer->init(connectParam);
}
std::cout << "连接地址:" << connectParam << endl;
if (_loop == nullptr)
{
std::cerr << "【初始化连接】事件循环无效,无法初始化连接" << std::endl;
return false;
}
std::string exchangeName = _exchangeName;
try
{
_handler = std::make_unique<CustomLibEvHandler>(_loop);
AMQP::Address address(connectParam);
_connection = std::make_unique<AMQP::TcpConnection>(_handler.get(), address);
// 启动事件循环线程(关键步骤)
_io_thread = std::thread([=] {
ev_run(_loop, 0); // 阻塞在此处理网络事件
});
_channel = std::make_unique<AMQP::TcpChannel>(_connection.get());
#if 1
if(exchangeName != ""){//如果交换机名称为空,则不发送广播消息
// 声明fanout类型交换机(广播专用)
_channel->declareExchange(exchangeName, AMQP::fanout,AMQP::durable | AMQP::autodelete)
.onSuccess([=](){
std::cerr << "交换机声明成功: " << exchangeName << std::endl;
})
.onError([=](const char* error){
std::cerr << "交换机声明失败: " << error << std::endl;
});
}
#endif
// 改造核心:队列声明成功后,标记就绪并触发批量发送
if(_durable){
_channel->declareQueue(
std::string_view(_productQueueName.data(), _productQueueName.size()),
AMQP::durable
)
.onSuccess([=](const std::string &name, uint32_t message_count, uint32_t consumer_count) {
std::cout << "【初始化连接】队列声明成功:" << name
<< ",当前消息数:" << message_count
<< ",消费者数:" << consumer_count << std::endl;
_isInitSuccess = true; // 标记初始化成功
// 关键:队列就绪后,立即发送缓存中的消息
this->_sendBatchMessages();
})
.onError([this](const char *message) {
std::cerr << "【初始化连接】队列声明失败:" << message << std::endl;
_isInitSuccess = false;
if (_loop != nullptr)
{
ev_break(_loop, EVBREAK_ALL);
}
});
}else{
_channel->declareQueue(
std::string_view(_productQueueName.data(), _productQueueName.size()),
0
)
.onSuccess([=](const std::string &name, uint32_t message_count, uint32_t consumer_count) {
std::cout << "【初始化连接】队列声明成功:" << name
<< ",当前消息数:" << message_count
<< ",消费者数:" << consumer_count << std::endl;
_isInitSuccess = true; // 标记初始化成功
// 关键:队列就绪后,立即发送缓存中的消息
this->_sendBatchMessages();
})
.onError([this](const char *message) {
std::cerr << "【初始化连接】队列声明失败:" << message << std::endl;
_isInitSuccess = false;
if (_loop != nullptr)
{
ev_break(_loop, EVBREAK_ALL);
}
});
}
return true;
}
catch (const std::exception &e)
{
std::cerr << "【初始化连接】异常:" << e.what() << std::endl;
return false;
}
}
2. 发送单播消息接口实现
cpp
bool RabbitMQAdapter::sendMessage(const string &message)
{
AMQP::Envelope envelope(message.data(), message.size());
envelope.setDeliveryMode(2);
bool sendResult = _channel->publish("", _productQueueName, envelope);
if (sendResult)
{
std::cout << "【发送消息】成功:" << message << std::endl;
return true;
}
else
{
std::cerr << "【发送消息】失败:" << message << std::endl;
return false;
}
}
3.发送广播消息接口实现
cpp
bool RabbitMQAdapter::sendBroadcast(const string &exchangeName, const string &message)
{
if(!_channel->usable())
return false;
// 创建消息信封
AMQP::Envelope envelope(message.data(), message.size());
envelope.setDeliveryMode(2); // 持久化消息
// 发送广播消息(路由键为空)
_channel->publish(exchangeName, "", envelope);
std::cout << "广播消息已发送: " << message << std::endl;
return true;
}
4.开启消费队列接口实现
cpp
void RabbitMQAdapter::startConsuming()
{
if(_consumer){
_consumer->doConsuming();
}
}
void RabbitMQConsumer::doConsuming()
{
_consumThread = std::thread([=](){
this->ConsumingImpl();
});
}
void RabbitMQConsumer::ConsumingImpl()
{
RabbitMQConsumer *ctx = this;
// 1. 获取LibEv默认事件循环
struct ev_loop *loop = ev_loop_new(EVFLAG_AUTO);
if (!loop)
{
std::cerr << "获取libev事件循环失败" << std::endl;
return ;
}
// 2. 创建自定义LibEv处理器
CustomLibEvHandler handler(loop);
// 3. 创建TCP连接(与生产者使用相同的RabbitMQ配置)
AMQP::Address address(_connectParam);
AMQP::TcpConnection connection(&handler, address);
// 4. 创建Channel(队列声明、消息消费的核心载体)
AMQP::TcpChannel channel(&connection);
// 5. 定义队列名称(与生产者保持一致,若使用持久化队列则对应修改名称)
std::string queue_name = _consumeQueueName; // 若生产者使用了 test_queue_ev_durable,此处需对应修改
// 6. 创建自定义消息处理器实例
CustomMessageHandler messageHandler(&channel);
// 7. 声明队列(与生产者队列参数保持一致,避免配置冲突)
// 若生产者已创建持久化队列,此处添加 AMQP::durable;否则使用 0(非持久化)
if(_recvQueueDurable){
channel.declareQueue(
std::string_view(queue_name.data(), queue_name.size()),
AMQP::durable // 与生产者队列配置保持一致,若为持久化队列则改为 AMQP::durable
)
.onSuccess([&channel, &messageHandler,queue_name,ctx](
const std::string &name,
uint32_t message_count,
uint32_t consumer_count) {
std::cout << "队列声明成功(消费者):" << name << std::endl;
std::cout << "队列当前消息数:" << message_count << ",当前消费者数:" << consumer_count << std::endl;
//7.1 绑定交换机与队列
#if 1
std::string exchangeName = ctx->_recvExchangeName;
if(exchangeName != ""){
const AMQP::Table table;
channel.bindQueue(exchangeName,ctx->_consumeQueueName,"",table)
.onSuccess([=](){
std::cout << "接收方:绑定交换机:" << exchangeName <<" 与消息队列:" << ctx->_consumeQueueName <<"成功" << std::endl;
})
.onError([=](const char *message){
std::cout << "接收方:绑定交换机:" << exchangeName <<" 与消息队列:" << ctx->_consumeQueueName <<"失败; 错误信息:" << message << std::endl;
});
}
#endif
// 8. 开始消费队列中的消息(核心消费逻辑)
channel.consume(queue_name)
// 核心修改:1. 修正 Message 为 const 引用 2. 确保捕获列表清晰 3. 签名与标准完全一致
.onMessage([&messageHandler,ctx](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) {
// 转发消息到自定义消息处理器(逻辑不变,仅参数传递符合标准签名)
messageHandler.onMessageReceived(message, deliveryTag, redelivered);
if(ctx && ctx->callback_){
std::string messageContent(message.body(), message.bodySize());
ctx->callback_(messageContent);
}
})
.onSuccess([](const std::string &consumerTag) {
std::cout << "消费启动成功,消费者标签(consumerTag):" << consumerTag << std::endl;
})
.onError([](const char *message) {
std::cerr << "消费启动失败:" << message << std::endl;
});
})
.onError([loop](const char *message) {
std::cerr << "队列声明失败(消费者):" << message << std::endl;
if (loop != nullptr)
{
ev_break(loop, EVBREAK_ALL); // 声明失败,退出事件循环
}
});
}else{
channel.declareQueue(
std::string_view(queue_name.data(), queue_name.size()),
0
)
.onSuccess([&channel, &messageHandler,queue_name,ctx](const std::string &name, uint32_t message_count, uint32_t consumer_count) {
std::cout << "队列声明成功(消费者):" << name << std::endl;
std::cout << "队列当前消息数:" << message_count << ",当前消费者数:" << consumer_count << std::endl;
//7.1 绑定交换机与队列
#if 1
std::string exchangeName = ctx->_recvExchangeName;
if(exchangeName != ""){
const AMQP::Table table;
channel.bindQueue(exchangeName,ctx->_consumeQueueName,"",table)
.onSuccess([=](){
std::cout << "接收方:绑定交换机:" << exchangeName <<" 与消息队列:" << ctx->_consumeQueueName <<"成功" << std::endl;
})
.onError([=](const char *message){
std::cout << "接收方:绑定交换机:" << exchangeName <<" 与消息队列:" << ctx->_consumeQueueName <<"失败; 错误信息:" << message << std::endl;
});
}
#endif
// 8. 开始消费队列中的消息(核心消费逻辑)
channel.consume(queue_name)
// 核心修改:1. 修正 Message 为 const 引用 2. 确保捕获列表清晰 3. 签名与标准完全一致
.onMessage([&messageHandler,ctx](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) {
// 转发消息到自定义消息处理器(逻辑不变,仅参数传递符合标准签名)
messageHandler.onMessageReceived(message, deliveryTag, redelivered);
if(ctx && ctx->callback_){
std::string messageContent(message.body(), message.bodySize());
ctx->callback_(messageContent);
}
})
.onSuccess([](const std::string &consumerTag) {
std::cout << "消费启动成功,消费者标签(consumerTag):" << consumerTag << std::endl;
})
.onError([](const char *message) {
std::cerr << "消费启动失败:" << message << std::endl;
});
})
.onError([loop](const char *message) {
std::cerr << "队列声明失败(消费者):" << message << std::endl;
if (loop != nullptr)
{
ev_break(loop, EVBREAK_ALL); // 声明失败,退出事件循环
}
});
}
// 9. 启动LibEv事件循环,持续监听并消费消息(无阻塞,一直运行直到手动终止)
std::cout << "消费者启动成功,开始监听队列消息(按 Ctrl+C 终止)..." << std::endl;
ev_run(loop, 0);
// 10. 事件循环退出后的清理(此处仅打印日志,可添加自定义清理逻辑)
std::cout << "消费者运行结束" << std::endl;
}
四 调用流程
a.创建一个消息队列实例;
b.设置发送消息参数,设置接收消息参数;
c.连接RabbitMQ服务;
d.开启消费队列(可选);
e.发送单播(广播)消息;
f.断开连接.
测试程序如下:
cpp
#include <QCoreApplication>
#include "imsmessagequeue.h"
#include <QDebug>
#include <QTimer>
#define SEND_QUEUENAME "alarm-queue"
#define RECV_QUEUENAME "alarm-queue"
#define HOST "192.168.22.196"
#define PORT 5672
#define USERNAME "admin"
#define PASSWORD "123456"
#define VHOST "/"
static void dealRecvMsg(const std::string msg);
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
std::string exchangeName = "broadcast.notify";
IMSMessageQueue *_msgQueue = new IMSMessageQueue();
_msgQueue->SetSendParams(SEND_QUEUENAME,exchangeName);
_msgQueue->SetRecvParams(RECV_QUEUENAME,dealRecvMsg,exchangeName);
bool ret = _msgQueue->Connect(HOST,PORT,USERNAME,PASSWORD,VHOST);
if(ret){
qDebug() << "连接服务成功";
}else{
qDebug() << "连接服务失败";
}
_msgQueue->StartConsuming();
QTimer::singleShot(5000,[=](){
_msgQueue->Send("这是一条单播消息1 ");
_msgQueue->Send("这是一条单播消息2 ");
_msgQueue->Send("这是一条单播消息3");
_msgQueue->Send("这是一条单播消息4");
_msgQueue->SendBroadcast(exchangeName,"这是一个广播消息");
_msgQueue->SendBroadcast(exchangeName,"这是一个广播消息1");
});
return a.exec();
}
void dealRecvMsg(const std::string msg)
{
qDebug() << "接收到消息:" << msg.c_str();
}
五 运行效果
ProductMQ运行结果:

ConsumeMQ运行结果:
