目录
一.Consumer模块介绍
设计思路
在消息队列系统中,消费者用于订阅队列中的消息并进行处理。为了更好地管理多个消费者,我们设计了以下几个类:
- Consumer:消费者类,用于定义消费者的基本属性和回调函数。
- QueueConsumer:队列消费者管理类,用于管理同一队列中的多个消费者,采用RR轮转机制来选择消费者。
- ConsumerManager:消费者管理器,用于管理所有队列中的消费者,并提供接口来创建、删除和选择消费者。
二.Consumer的定义
Consumer
类封装了一个消费者的基本属性,包括消费者标识、订阅的队列名称、是否自动确认、以及一个回调函数。其主要代码如下:
- ptr:定义了一个类型
Consumer::ptr
,表示std::shared_ptr<Consumer>
,方便后续使用智能指针管理Consumer
对象。 - ConsumerCallBack:回调函数类型,接收三个参数,分别为消息的标签、消息的基本属性和消息内容,用户可以自定义回调逻辑。
- 构造函数:初始化
Consumer
对象时设置消费者标识、队列名称、自动确认标志以及回调函数。(消费者调用回调函数,将收到的消息推送给客户端) - 析构函数:销毁
Consumer
对象时记录日志,方便调试。
struct Consumer {
using ptr = std::shared_ptr<Consumer>; // 定义一个智能指针类型
using ConsumerCallBack = std::function<void(const std::string &, const msg::BasicAttributes *, const std::string &)>;
std::string _tag; // 消费者的标识,唯一标识一个消费者
std::string _qname; // 队列名称,表示该消费者订阅的消息队列
bool _auto_ack; // 自动确认标志,表明消费者是否自动确认消息
ConsumerCallBack _cb; // 消费者的回调函数,接收消息后调用
// 构造函数,初始化消费者的基本属性
Consumer(const std::string &ctag, const std::string &qname, bool auto_ack, const ConsumerCallBack &cb)
: _tag(ctag), _qname(qname), _auto_ack(auto_ack), _cb(cb) {
DLOG("consumer:%p created", this); // 日志记录,消费者创建时输出信息
}
// 析构函数,在对象销毁时调用
~Consumer() {
DLOG("consumer:%p destroyed", this); // 日志记录,消费者销毁时输出信息
}
};
三.QueueConsumer的实现
QueueConsumer
类用于管理同一队列中的多个消费者,并实现了一个简单的RR轮转选择机制,以确保每个消费者都有机会处理消息。
成员变量:
std::string _qname; // 队列名称
uint64_t _rrSeq = 0; // rr轮转的序号
std::vector<Consumer::ptr> _consumers;
std::mutex _mutex;
代码细节解释:
- create:用于创建新的消费者。首先检查消费者是否已经存在,如果存在则返回已有消费者指针;如果不存在,则创建新消费者并加入到
_consumers
列表中。 - choose:实现了轮转机制,每次调用该函数时,选择
_consumers
列表中的下一个消费者。通过rrSeq
来记录当前的轮转位置。 - remove:用于删除指定的消费者。通过标识查找到消费者,并从列表中删除。如果未找到消费者,会输出一条日志。
- exists:用于检查消费者是否存在。通过遍历
_consumers
列表,检查是否存在指定标识的消费者。 - empty:用于检查
_consumers
列表是否为空。如果没有消费者,返回true
,否则返回false
。 - clear:清空
_consumers
列表并重置轮转序号。
class QueueConsumer {
private:
std::string _qname; // 队列名称,表示管理的消费者对应的消息队列
uint64_t _rrSeq = 0; // 轮转序列号,用于轮转选择消费者
std::vector<Consumer::ptr> _consumers; // 消费者列表,存储所有订阅该队列的消费者
std::mutex _mutex; // 互斥锁,确保在多线程环境下的线程安全
public:
using ptr = std::shared_ptr<QueueConsumer>; // 定义智能指针类型
// 构造函数,初始化队列名称
QueueConsumer(const std::string &_qname)
: _qname(_qname) {}
// 创建消费者,若消费者已存在则返回已有的消费者指针
Consumer::ptr create(const std::string &ctag, bool auto_ack, const Consumer::ConsumerCallBack &cb) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
// 遍历已有消费者,检查是否已经存在具有相同标识的消费者
for (const auto &cptr : _consumers) {
if (cptr->_tag == ctag) // 如果存在,返回该消费者指针
return cptr;
}
// 如果不存在,创建新的消费者并加入列表
auto ret = std::make_shared<Consumer>(ctag, _qname, auto_ack, cb);
_consumers.push_back(ret);
return ret;
}
// 轮转选择消费者,每次调用时依次选择不同的消费者
Consumer::ptr choose() {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
if (_consumers.empty()) // 如果没有消费者,返回空指针
return Consumer::ptr();
// 根据轮转序号选择消费者,并递增轮转序号
uint64_t index = _rrSeq % _consumers.size();
_rrSeq++;
return _consumers[index]; // 返回选择的消费者指针
}
// 删除消费者,根据标识查找并删除指定的消费者
void remove(const std::string &ctag) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
// 遍历消费者列表,找到指定标识的消费者并删除
for (auto it = _consumers.begin(); it != _consumers.end(); it++) {
if ((*it)->_tag == ctag) {
_consumers.erase(it);
return;
}
}
DLOG("consumer:%s not found", ctag.c_str()); // 未找到指定消费者时输出日志
}
// 检查消费者是否存在,根据标识查找
bool exists(const std::string &ctag) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
// 遍历消费者列表,检查是否存在指定标识的消费者
for (const auto &cptr : _consumers) {
if (cptr->_tag == ctag)
return true;
}
return false; // 不存在返回 false
}
// 检查消费者列表是否为空
bool empty() {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
return _consumers.empty(); // 返回是否为空
}
// 清空消费者列表,重置轮转序号
void clear() {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
_consumers.clear(); // 清空列表
_rrSeq = 0; // 重置轮转序号
}
};
四.ConsumerManager的实现
成员变量:
std::unordered_map<std::string, QueueConsumer::ptr> _qconsumers;
std::mutex _mutex;
代码细节解释:
- initQueueConsumer:初始化队列消费者。如果
_qconsumers
中已经存在对应队列名的QueueConsumer
,则无需创建;否则,创建一个新的QueueConsumer
对象并加入到_qconsumers
中管理。 - destroyQueueConsumer:销毁指定队列的
QueueConsumer
对象,从_qconsumers
中删除。 - create:为指定队列创建消费者,调用
QueueConsumer::create()
来创建新的消费者。 - choose:轮转选择消费者,调用
QueueConsumer::choose()
来实现消费者的轮转选择。 - remove:删除指定的消费者,调用
QueueConsumer::remove()
。 - exists:检查消费者是否存在,调用
QueueConsumer::exists()
。 - empty:检查队列的消费者列表是否为空,调用
QueueConsumer::empty()
。 - clear:清空指定队列的消费者列表,调用
QueueConsumer::clear()
。
以上过程都通过加锁来保证线程完全,并加入了错误处理日志打印。
class ConsumerManager {
private:
std::unordered_map<std::string, QueueConsumer::ptr> _qconsumers; // 存储队列名与其对应的消费者列表
std::mutex _mutex; // 互斥锁,保护共享资源的线程安全
public:
using ptr = std::shared_ptr<ConsumerManager>; // 定义智能指针类型
// 构造函数,初始化消费者管理器
ConsumerManager() {}
// 初始化队列消费者,确保为每个队列创建唯一的 QueueConsumer 对象
void initQueueConsumer(const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
// 如果该队列已经有了 QueueConsumer 对象,则无需再次创建
if (_qconsumers.find(qname) != _qconsumers.end()) {
DLOG("init: queue consumer:%s already exists", qname.c_str());
return;
}
// 如果不存在,创建新的 QueueConsumer 对象并加入到 _qconsumers 列表中
auto ret = std::make_shared<QueueConsumer>(qname);
_qconsumers.insert({qname, ret});
}
// 销毁指定队列的 QueueConsumer 对象
void destroyQueueConsumer(const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
_qconsumers.erase(qname); // 从 _qconsumers 列表中删除对应的队列消费者
}
// 创建消费者,调用 QueueConsumer::create(),为指定队列创建消费者
Consumer::ptr create(const std::string &ctag, const std::string &qname, bool auto_ack, const Consumer::ConsumerCallBack &cb) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
return it->second->create(ctag, auto_ack, cb); // 如果找到,创建消费者并返回
}
return Consumer::ptr(); // 如果未找到,返回空指针
}
// 选择消费者,调用 QueueConsumer::choose(),轮转选择消费者
Consumer::ptr choose(const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
return it->second->choose(); // 如果找到,轮转选择消费者并返回
}
return Consumer::ptr(); // 如果未找到,返回空指针
}
// 删除消费者,调用 QueueConsumer::remove(),根据标识删除指定消费者
void remove(const std::string &ctag, const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
it->second->remove(ctag); // 如果找到,删除指定消费者
}
}
// 检查消费者是否存在,调用 QueueConsumer::exists(),根据标识检查消费者
bool exists(const std::string &ctag, const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
return it->second->exists(ctag); // 如果找到,检查消费者是否存在
}
return false; // 如果未找到,返回 false
}
// 检查队列消费者列表是否为空
bool empty(const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
return it->second->empty(); // 如果找到,检查消费者列表是否为空
}
return true; // 如果未找到,返回 true
}
// 清空所有队列的消费者列表
void clear(const std::string &qname) {
std::unique_lock<std::mutex> lock(_mutex); // 加锁,确保线程安全
auto it = _qconsumers.find(qname); // 查找对应的 QueueConsumer 对象
if (it != _qconsumers.end()) {
it->second->clear(); // 如果找到,清空消费者列表
}
}
};
五.全部代码
#pragma once
#include "../common_mq/helper.hpp"
#include "../common_mq/logger.hpp"
#include "../common_mq/msg.pb.h"
#include <unordered_map>
#include <mutex>
#include <memory>
#include <cassert>
#include <cstring>
#include <vector>
#include <functional>
namespace mq
{
struct Consumer
{
using ptr = std::shared_ptr<Consumer>;
// tag BasicAttributes body
using ConsumerCallBack = std::function<void(const std::string &,const msg::BasicAttributes *, const std::string &)>;
std::string _tag; // 消费者标识
std::string _qname; // 订阅的队列的名称
bool _auto_ack; // 是否自动确认
ConsumerCallBack _cb; // 消费者回调函数
Consumer(const std::string &ctag, const std::string &qname, bool auto_ack,const ConsumerCallBack &cb)
: _tag(ctag), _qname(qname), _auto_ack(auto_ack), _cb(cb)
{
DLOG("consumer:%p created",this);
}
Consumer() {
DLOG("consumer:%p created",this);
}
~Consumer() {
DLOG("consumer:%p destroyed",this);
}
};
class QueueConsumer
{
private:
std::string _qname; // 队列名称
uint64_t _rrSeq = 0; // rr轮转的序号
std::vector<Consumer::ptr> _consumers;
std::mutex _mutex;
public:
using ptr = std::shared_ptr<QueueConsumer>;
QueueConsumer(const std::string &_qname)
: _qname(_qname) {}
// 1.新增/删除消费者
Consumer::ptr create(const std::string &ctag, bool auto_ack,const Consumer::ConsumerCallBack &cb)
{
std::unique_lock<std::mutex> lock(_mutex);
// a.存在就返回,不存在则新增
for (const auto &cptr : _consumers)
{
if (cptr->_tag == ctag)
return cptr;
}
// b.新增
auto ret = std::make_shared<Consumer>(ctag, _qname, auto_ack, cb);
_consumers.push_back(ret);
return ret;
}
// 2.获取一个消费者(按照rr轮转序号)
Consumer::ptr choose()
{
std::unique_lock<std::mutex> lock(_mutex);
// a.判断是否为空
if (_consumers.empty())
return Consumer::ptr();
// b.按照rr轮转序号获取
uint64_t index = _rrSeq % _consumers.size();
_rrSeq++;
return _consumers[index];
}
void remove(const std::string &ctag)
{
std::unique_lock<std::mutex> lock(_mutex);
// a.存在就删除,不存在则返回
for (auto it = _consumers.begin(); it != _consumers.end(); it++)
{
if ((*it)->_tag == ctag)
{
_consumers.erase(it);
return;
}
}
DLOG("consumer:%s not found", ctag.c_str());
}
// 3.判断存在/空/clear
bool exists(const std::string &ctag)
{
std::unique_lock<std::mutex> lock(_mutex);
// a.存在就返回,不存在则新增
for (const auto &cptr : _consumers)
{
if (cptr->_tag == ctag)
return true;
}
return false;
}
bool empty()
{
std::unique_lock<std::mutex> lock(_mutex);
return _consumers.empty();
}
void clear()
{
std::unique_lock<std::mutex> lock(_mutex);
_consumers.clear();
_rrSeq = 0;
}
};
class ConsumerManager
{
private:
std::unordered_map<std::string, QueueConsumer::ptr> _qconsumers;
std::mutex _mutex;
public:
using ptr = std::shared_ptr<ConsumerManager>;
ConsumerManager() {}
// 1.创建/销毁队列消费者
void initQueueConsumer(const std::string &qname)
{
std::unique_lock<std::mutex> lock(_mutex);
// a.存在就返回,不存在则新增
for (const auto &qptr : _qconsumers)
{
if (qptr.first == qname)
{
DLOG("init: queue consumer:%s already exists", qname.c_str());
return;
}
}
// b.新增
auto ret = std::make_shared<QueueConsumer>(qname);
_qconsumers.insert(std::make_pair(qname, ret));
}
void destroyQueueConsumer(const std::string &qname)
{
std::unique_lock<std::mutex> lock(_mutex);
_qconsumers.erase(qname);
}
// 2.向指定队列 新增/删除 消费者
Consumer::ptr create(const std::string &qname, const std::string &ctag, bool auto_ack,const Consumer::ConsumerCallBack &cb)
{
QueueConsumer::ptr ret;
{
std::unique_lock<std::mutex> lock(_mutex);
// a.先判断是否存在该队列
auto it = _qconsumers.find(qname);
if (it == _qconsumers.end())
{
DLOG("queue consumer:%s not found", qname.c_str());
return Consumer::ptr();
}
ret = it->second;
}
// b.存在则构建消费者后插入
return ret->create(ctag, auto_ack, cb);
}
void remove(const std::string &qname, const std::string &ctag)
{
QueueConsumer::ptr ret;
{
std::unique_lock<std::mutex> lock(_mutex);
// a.先判断是否存在该队列
auto it = _qconsumers.find(qname);
if (it == _qconsumers.end())
{
DLOG("queue consumer:%s not found", qname.c_str());
return;
}
ret = it->second;
}
// b.移除该队列中的消费者
return ret->remove(ctag);
}
// 3.获取一个消费者(按照rr轮转序号)
Consumer::ptr choose(const std::string &qname)
{
QueueConsumer::ptr ret;
{
std::unique_lock<std::mutex> lock(_mutex);
// a.先判断是否存在该队列
auto it = _qconsumers.find(qname);
if (it == _qconsumers.end())
{
DLOG("queue consumer:%s not found", qname.c_str());
return Consumer::ptr();
}
ret = it->second;
}
// b.获取一个消费者(按照rr轮转序号)
return ret->choose();
}
// 4.测试相关
bool exists(const std::string &qname, const std::string &ctag) // 队列中的某个消费者是否存在
{
QueueConsumer::ptr ret;
{
std::unique_lock<std::mutex> lock(_mutex);
// a.先判断是否存在该队列
auto it = _qconsumers.find(qname);
if (it == _qconsumers.end())
{
DLOG("queue consumer:%s not found", qname.c_str());
return false;
}
ret = it->second;
}
// b.判断消费者是否存在
return ret->exists(ctag);
}
bool empty(const std::string &qname)
{
QueueConsumer::ptr ret;
{
std::unique_lock<std::mutex> lock(_mutex);
// a.先判断是否存在该队列
auto it = _qconsumers.find(qname);
if (it == _qconsumers.end())
{
DLOG("empty: queue consumer:%s not found", qname.c_str());
return true;
}
ret = it->second;
}
// b.存在,即不为空
return ret->empty();
}
void clear()
{
std::unique_lock<std::mutex> lock(_mutex);
_qconsumers.clear();
}
};
};