rabbitMq-----消费者管理模块

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

只有订阅客户端订阅了一个队列消息后,他才是一个消费者,而消费者存在的意义是,当这个队列上有消息的时候我们呢需要给订阅了这个队列的客户端推送消息,也就需要获取客户端的连接,所以我们呢可以通过消费者来找到对应的连接来进行消息的推送。


消费者字段

消费者订阅的队列名称,消费者也是按照队列问单元进行管理的。

消费者标志,标志消费者唯一性

自动应答标志,客户端在订阅队列消息时可以进行设置,如果为true,则将消息推送给消费客户端后不需要等待应带,直接将消息进行删除。

还有一个回调函数。当队列有消息到来时,就需要选择一个队列消费者进行消费,那么如何进行消费呢?就是调用这个回调函数。这个函数的逻辑固定,就是通过连接发送消息。是由我们broker服务器设置的。

cpp 复制代码
//当队列收到一条消息后,需要选择一个消费者进行消费,那么如何消费呢?
    //就是调用这个回调函数,其内部逻辑是:找到消费者对应的连接,将消息发送给客户端。
    using ConsumerCallBack = std::function<void(const std::string&,const BasicProperties *,const std::string &)>;
    struct Consumer
    {
        using ptr = std::shared_ptr<Consumer>;
        std::string _qname; //消费者订阅的队列名称
        std::string _ctag;  //消费者标识
        bool _auto_ack;  //自动应答标志
        ConsumerCallBack _cb;
}

消费者内存管理类

消费者是以队列为单位进行管理的,我们使用一个vector管理订阅该队列的消费者。

目的是方便进行rr轮转。

cpp 复制代码
class QueueConsumer
    {
    private:
        std::string _qname; //队列名称
        std::mutex _mutex;  
        uint64_t _rr_sep;   //轮转序号
        std::vector<Consumer::ptr> _consumers;
    }

新增/删除消费者

构造一个消费者对象进行添加。删除就是遍历删除。

cpp 复制代码
 //新增一个消费者 
        Consumer::ptr create(const std::string &qname,const std::string &ctag,bool auto_ack,const ConsumerCallBack &cb){
   //1.加锁
   std::unique_lock<std::mutex> lock(_mutex);

   //2.判断消费者是否存在
   for(auto &cons : _consumers){
       if(cons->_ctag == ctag){
           return cons;
       }
   }

   //3.创建消费者并添加管理
   Consumer::ptr csp = std::make_shared<Consumer>(qname,ctag,auto_ack,cb);
   _consumers.push_back(csp);

   return csp;
}
//删除一个消费者:取消订阅、信道关闭、连接关闭的时候删除
void remove(const std::string &ctag){
   //1.加锁
   std::unique_lock<std::mutex> lock(_mutex);

   //2.遍历删除
   for(auto it = _consumers.begin(); it != _consumers.end(); ++it){
       if((*it)->_ctag == ctag){
           _consumers.erase(it);
           return;
       }
   }

   return;
}

选择一个消费者,rr轮转的思想。

在收到一条消息后,会进行路由匹配,获取到匹配成功的队列。然后把消息推送到队列上,同时取出一个消费者进行消费。

cpp 复制代码
 //选择一个消费者,rr轮转的思想
 Consumer::ptr choose(){
     //1.加锁
     std::unique_lock<std::mutex> lock(_mutex);

     //如果没有消费者,则返回空对象
     if(_consumers.size() == 0){
         return Consumer::ptr();
     }
     //2.获取当前选择的消费者下标
     uint64_t idx = _rr_sep % _consumers.size();
     _rr_sep++;

     //3.获取对象并返回
     return _consumers[idx];
 }

总的消费者管理类

这个类才是对外提供的类。

就是用一个哈希表把队列名称和消费者管理对象关联起来进行管理。

有一initQueueConsumer函数就是用力啊初始化队列消费者管理对象的,需要判断当前以存在的队列,来进行初始化对应的消费者管理类。

cpp 复制代码
 class ConsumerManager
    {
    private:    
        std::mutex _mutex;
        std::unordered_map<std::string,QueueConsumer::ptr> _qconsumers;
    public:
    using ptr = std::shared_ptr<ConsumerManager>;
        ConsumerManager(){}
        //初始化队列消费者管理
        void initQueueConsumer(const std::string &qname){
            //1. 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            //2. 重复判断
            auto it = _qconsumers.find(qname);
            if (it != _qconsumers.end()) {
                return ;
            }
            //3. 新增
            auto qconsumers = std::make_shared<QueueConsumer>(qname);
            _qconsumers.insert(std::make_pair(qname, qconsumers));
        }
}

删除队列消费者管理类对象,当一个队列删除时,就选哦删除这个队列的消费者管理类对象,

cpp 复制代码
 //删除队列消费者管理
        void destroyQueueConsumer(const std::string &qname){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _qconsumers.find(qname);
            if (it == _qconsumers.end()) {
                return ;
            }
            _qconsumers.erase(it);
        }

创建/删除指定队列的消费者管理类对象中的消费者

根据队列的名称,选择指定的消费者管理类对象进行一个操作即可,

cpp 复制代码
Consumer::ptr create(const std::string &qname,const std::string &ctag,bool auto_ack,const ConsumerCallBack &cb){
   QueueConsumer::ptr qcp;
   {
       std::unique_lock<std::mutex> lock(_mutex);

       auto it = _qconsumers.find(qname);
       if(it == _qconsumers.end()){
           ELOG("没有找到队列 %s 的消费者管理句柄!",qname.c_str());
           return Consumer::ptr();
       }

       qcp = it->second;
   }

   //这个create不需要我们这里的mutex保护,他有自己的mutex
   return qcp->create(qname,ctag,auto_ack,cb);
}
void remove(const std::string &ctag,const std::string &qname){
   QueueConsumer::ptr qcp;
   {
       std::unique_lock<std::mutex> lock(_mutex);

       auto it = _qconsumers.find(qname);
       if(it == _qconsumers.end()){
           ELOG("没有找到队列 %s 的消费者管理句柄!",qname.c_str());
           return ;
       }

       qcp = it->second;
   }

   qcp->remove(ctag);
}

选择指定队列的消费者管理类对象中的消费者

cpp 复制代码
Consumer::ptr choose(const std::string &qname){
   QueueConsumer::ptr qcp;
   {
       std::unique_lock<std::mutex> lock(_mutex);

       auto it = _qconsumers.find(qname);
       if(it == _qconsumers.end()){
           ELOG("没有找到队列 %s 的消费者管理句柄!",qname.c_str());
           return Consumer::ptr();
       }

       qcp = it->second;
   }

   return qcp->choose();
}
相关推荐
回家路上绕了弯2 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
用户8307196840822 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
用户8307196840824 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者5 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者7 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧8 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖8 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农8 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者8 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀8 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式