consumer.hpp消费者管理模块

目录

一.Consumer模块介绍

设计思路

二.Consumer的定义

三.QueueConsumer的实现

代码细节解释:

四.ConsumerManager的实现

代码细节解释:

五.全部代码

一.Consumer模块介绍

设计思路

在消息队列系统中,消费者用于订阅队列中的消息并进行处理。为了更好地管理多个消费者,我们设计了以下几个类:

  1. Consumer:消费者类,用于定义消费者的基本属性和回调函数。
  2. QueueConsumer:队列消费者管理类,用于管理同一队列中的多个消费者,采用RR轮转机制来选择消费者。
  3. 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;
代码细节解释:
  1. create:用于创建新的消费者。首先检查消费者是否已经存在,如果存在则返回已有消费者指针;如果不存在,则创建新消费者并加入到 _consumers 列表中。
  2. choose:实现了轮转机制,每次调用该函数时,选择 _consumers 列表中的下一个消费者。通过 rrSeq 来记录当前的轮转位置。
  3. remove:用于删除指定的消费者。通过标识查找到消费者,并从列表中删除。如果未找到消费者,会输出一条日志。
  4. exists:用于检查消费者是否存在。通过遍历 _consumers 列表,检查是否存在指定标识的消费者。
  5. empty:用于检查 _consumers 列表是否为空。如果没有消费者,返回 true,否则返回 false
  6. 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;
代码细节解释:
  1. initQueueConsumer:初始化队列消费者。如果 _qconsumers 中已经存在对应队列名的 QueueConsumer,则无需创建;否则,创建一个新的 QueueConsumer 对象并加入到 _qconsumers 中管理。
  2. destroyQueueConsumer:销毁指定队列的 QueueConsumer 对象,从 _qconsumers 中删除。
  3. create:为指定队列创建消费者,调用 QueueConsumer::create() 来创建新的消费者。
  4. choose:轮转选择消费者,调用 QueueConsumer::choose() 来实现消费者的轮转选择。
  5. remove:删除指定的消费者,调用 QueueConsumer::remove()
  6. exists:检查消费者是否存在,调用 QueueConsumer::exists()
  7. empty:检查队列的消费者列表是否为空,调用 QueueConsumer::empty()
  8. 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();
        }
    };
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值