仿RabbitMq实现简易消息队列正式篇(消费者篇)

@TOC

消费者管理模块

客户端由两种:发布消息,订阅消息

因此订阅了指定队列消息的客户端才是一个消费者。

消费者数据存在的意义:当指定队列有了消息以后,就需要将消息推送给这个消费者客户端(推送的时候就需要找到这个客户端相关的信息--连接)

消费者信息:

消费者标识 --tag

订阅队列名称:当当前队列有消息就会推送给这个客户端,以及当客户端收到消息,需要对指定队列的消息进行确认

消费处理回调函数:队列有一个消息后,通过哪个函数进行处理

客户端消费者设计

#ifndef __M_CONSUMER_H__
#define __M_CONSUMER_H__
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"
#include "../mqcommon/msg.pb.h"
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <vector>
#include <functional>

namespace mymq
{
    using ConsumerCallback = std::function<void(const std::string, const BasicProperties* bp, const std::string)>;
    struct Consumer
    {
        using ptr = std::shared_ptr<Consumer>;
        std::string tag;    //消费者标识
        std::string qname;  //消费者订阅的队列名称
        bool auto_ack;      //自动确认标志
        ConsumerCallback callback;

        Consumer()
        {
            DLOG("new Consumer: %p", this);
        }
        Consumer(const std::string& ctag, const std::string& queue_name, bool auto_flag, const ConsumerCallback& cb)
                :tag(ctag), 
                qname(queue_name),
                auto_ack(auto_flag),
                callback(std::move(cb))
        {
            DLOG("new Consumer: %p", this);
        }
        ~Consumer()
        {
            DLOG("del Consumer: %p", this);
        }
    };
} 

#endif

服务端消费者管理

管理思想

以队列为单位进行管理

每个消费者订阅的都是指定队列的消息,消费者对消息进行确认也是以队列进行确认

最关键的是:当指定队列中有消息了,必然是获取订阅了这个队列的消费者信息进行消息推送

队列消费者管理结构

数据信息
  • 消费者数组
  • RR轮转盘
  • 存储消费者的队列名
管理操作
  • 新增消费者
  • RR轮转获取一个消费者
  • 删除消费者
  • 队列消费者数量
  • 是否为空
代码展示
    // 以队列为单元的消费者管理结构
    class QueueConsumer
    {
    public:
        using ptr = std::shared_ptr<QueueConsumer>;

        QueueConsumer(const std::string &qname)
            : _qname(qname),
              _rr_seq(0)
        {
        }

        // 队列新增消费者
        Consumer::ptr create(const std::string &ctag, const std::string &queue_name, bool ack_flag, const ConsumerCallback &cb)
        {
            // 1. 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            // 2. 判断消费者是否重复
            for (auto e : _consumers)
            {
                if (e->tag == ctag)
                {
                    return Consumer::ptr();
                }
            }
            // 3. 没有重复则新增 -- 构造对象
            auto consumer = std::make_shared<Consumer>(ctag, _qname, ack_flag, cb);
            // 4. 添加管理后返回对象
            _consumers.push_back(consumer);
            return consumer;
        }

        // 队列移除消费者
        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)->tag == ctag)
                {
                    _consumers.erase(it);
                    return;
                }
            }
            return;
        }

        // 队列获取消费者:RR轮转获取
        Consumer::ptr choose()
        {
            // 1. 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            // 2. 获取当前轮转的下标
            if (_consumers.size() == 0)
            {
                return Consumer::ptr();
            }
            int index = _rr_seq % _consumers.size();
            // 3. 获取对象, 返回
            _rr_seq++;
            return _consumers[index];
        }

        // 是否为空
        bool empty()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _consumers.size() == 0;
        }

        // 判断指定消费者是否存在
        bool exists(const std::string &ctag)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for (auto it = _consumers.begin(); it != _consumers.end(); it++)
            {
                if ((*it)->tag == ctag)
                {
                    return true;
                }
            }
            return false;
        }

        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _consumers.clear();
            _rr_seq = 0;
        }

    private:
        std::string _qname;
        std::mutex _mutex;
        uint64_t _rr_seq;
        std::vector<Consumer::ptr> _consumers;
    };

管理操作

  • 初始化队列消费者结构

  • 删除队列消费者结构

  • 向指定队列添加消费者

  • 获取指定队列消费者

  • 删除指定队列消费者

    class ConsumerManager
    {
    public:
    using ptr = std::shared_ptr<ConsumerManager>;
    ConsumerManager()
    {
    }
    void initQueueConsumer(const std::string &qname)
    {
    // 1. 加锁
    std::unique_lockstd::mutex lock(_mutex);
    // 2. 重复判断
    auto it = _qconsumers.find(qname);
    if (it != _qconsumers.end())
    {
    return;
    }
    // 3. 新增
    QueueConsumer::ptr qcp = std::make_shared<QueueConsumer>(qname);
    _qconsumers.insert(std::make_pair(qname, qcp));
    }

          void destroyQueueConsumer(const std::string &qname)
          {
              std::unique_lock<std::mutex> lock(_mutex);
              _qconsumers.erase(qname);
          }
    
          // 这里不理解,为啥找到了还要进行创建
          Consumer::ptr create(const std::string &ctag, const std::string &queue_name, bool ack_flag, const ConsumerCallback &cb)
          {
              // 获取队列的消费者管理单元句柄,通过句柄完成新建
              QueueConsumer::ptr qcp;
              {
                  std::unique_lock<std::mutex> lock(_mutex);
                  auto it = _qconsumers.find(queue_name);
                  if (it == _qconsumers.end())
                  {
                      DLOG("没有找到队列 %s 的消费者管理句柄!", queue_name.c_str());
                      return Consumer::ptr();
                  }
                  qcp = it->second;
              }
              return qcp->create(ctag, queue_name, ack_flag, cb);
          }
    
          void remove(const std::string &ctag, const std::string &queue_name)
          {
              QueueConsumer::ptr qcp;
              {
                  std::unique_lock<std::mutex> lock(_mutex);
                  auto it = _qconsumers.find(queue_name);
                  if (it == _qconsumers.end())
                  {
                      DLOG("没有找到队列 %s 的消费者管理句柄!", queue_name.c_str());
                      return;
                  }
                  qcp = it->second;
              }
              return qcp->remove(ctag);
          }
    
          Consumer::ptr choose(const std::string &queue_name)
          {
              QueueConsumer::ptr qcp;
              {
                  std::unique_lock<std::mutex> lock(_mutex);
                  auto it = _qconsumers.find(queue_name);
                  if (it == _qconsumers.end())
                  {
                      DLOG("没有找到队列 %s 的消费者管理句柄!", queue_name.c_str());
                      return Consumer::ptr();
                  }
                  qcp = it->second;
              }
              return qcp->choose();
          }
          bool empty(const std::string &queue_name)
          {
              QueueConsumer::ptr qcp;
              {
                  std::unique_lock<std::mutex> lock(_mutex);
                  auto it = _qconsumers.find(queue_name);
                  if (it == _qconsumers.end())
                  {
                      DLOG("没有找到队列 %s 的消费者管理句柄!", queue_name.c_str());
                      return false;
                  }
                  qcp = it->second;
              }
              return qcp->empty();
          }
          bool exists(const std::string &ctag, const std::string &queue_name)
          {
              QueueConsumer::ptr qcp;
              {
                  std::unique_lock<std::mutex> lock(_mutex);
                  auto it = _qconsumers.find(queue_name);
                  if (it == _qconsumers.end())
                  {
                      DLOG("没有找到队列 %s 的消费者管理句柄!", queue_name.c_str());
                      return false;
                  }
                  qcp = it->second;
              }
              return qcp->exists(ctag);
          }
          void clear()
          {
              std::unique_lock<std::mutex> lock(_mutex);
              _qconsumers.clear();
          }
    
      private:
          std::mutex _mutex;
          std::unordered_map<std::string, QueueConsumer::ptr> _qconsumers;
      };
    

    }

相关推荐
全栈开发圈8 分钟前
干货分享|分布式数据科学工具 Xorbits 的使用
分布式
运维&陈同学2 小时前
【zookeeper01】消息队列与微服务之zookeeper工作原理
运维·分布式·微服务·zookeeper·云原生·架构·消息队列
时差9532 小时前
Flink Standalone集群模式安装部署
大数据·分布式·flink·部署
菠萝咕噜肉i3 小时前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁
只因在人海中多看了你一眼6 小时前
分布式缓存 + 数据存储 + 消息队列知识体系
分布式·缓存
zhixingheyi_tian8 小时前
Spark 之 Aggregate
大数据·分布式·spark
求积分不加C10 小时前
-bash: ./kafka-topics.sh: No such file or directory--解决方案
分布式·kafka
nathan052910 小时前
javaer快速上手kafka
分布式·kafka
谭震鸿13 小时前
Zookeeper集群搭建Centos环境下
分布式·zookeeper·centos
天冬忘忧18 小时前
Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化
大数据·分布式·kafka