订阅者模块

1. 客户端实现思路

在 RabbitMQ 中,提供服务的是信道,因此在客户端的实现中,弱化了 Client 客户端的概念,也就是说在 RabbitMQ 中并不会向用户展示网络通信的概念出来,而是以一种提供服务的形式来体现。

其实现思想类似于普通的功能接口封装,一个接口实现一个功能,接口内部完成向客户端请求的过程,但是对外并不需要体现出客户端与服务端通信的概念,用户需要什么服务就调用什么接口就行。

基于以上的思想,客户端的实现共分为四大模块:

  • 订阅者模块:
    • 一个并不直接对用户展示的模块,其在客户端体现的作用就是对于角色的描 述,表示这是一个消费者
  • 信道模块
    • 一个直接面向用户的模块,内部包含多个向外提供的服务接口,用户需要什么服务,调用对应接口即可○ 其包含交换机声明/删除,队列声明/删除,绑定/解绑,消息发布/确认,订阅/解除订阅等服务。
  • 连接模块
    • 这是唯一能体现出网络通信概念的一个模块了,它向用户提供的功能就是用于打开/关闭信道。
  • 异步线程模块
    • 虽然客户端部分,并不对外体现网络通信的概念,但是本质上内部还是包含有网络通信的,因此既然有网络通信,那么就必须包含有一个网络通信 IO 事件监控线程模块,用于进行客户端连接的 IO 事件监控,以便于在事件出发后进行 IO 操作。
    • 其次,在客户端部分存在一个情况就是,当一个信道作为消费者而存在的时 候,服务端会向信道推送消息,而用户这边需要对收到的消息进行不同的业务处理,而这个消息的处理需要一个异步的工作线程池来完成。
    • 因此异步线程模块包含两个部分:
      • 客户端连接的 IO 事件监控线程
      • 推送过来的消息异步处理线程

基于以上模块,实现一个客户端的流程也就比较简单了

  1. 实例化异步线程对象
  2. 实例化连接对象
  3. 通过连接对象,创建信道
  4. 根据信道获取自己所需服务
  5. 关闭信道
  6. 关闭连接

2. 订阅者模块

与服务端,并无太大差别,客户端这边虽然订阅者的存在感微弱了很多,但是还是有的,当进行队列消息订阅的时候,会伴随着一个订阅者对象的创建,而这个订阅者对象有以下几个作用:

  • 描述当前信道订阅了哪个队列的消息。
  • 描述了收到消息后该如何对这条消息进行处理。
  • 描述收到消息后是否需要进行确认回复。

所以,订阅者模块并不直接对用户展示,它是对消费者角色的描述。当用户通过信道订阅队列时,内部会创建一个订阅者对象,该对象负责处理从服务端推送过来的消息。

设计要点:

  1. 每个订阅者对应一个消费者标签(consumer_tag)和一个队列(queue_name)。
  2. 订阅者需要提供一个回调函数,当消息到达时,异步线程模块会调用这个回调函数。
  3. 订阅者可能支持自动确认或手动确认模式。

因此订阅者信息:

  • 订阅者标识
  • 订阅队列名
  • 是否自动确认标志
  • 回调处理函数(收到消息后该如何处理的回调函数对象)
cpp 复制代码
#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 rabbitmq
{
    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 ack_flag, const ConsumerCallback &cb)
            :_tag(ctag), _qname(queue_name), _auto_ack(ack_flag), _callback(std::move(cb)) 
        {
            DLOG("new Consumer: %p", this);
        }
        ~Consumer() 
        {
            DLOG("del Consumer: %p", this);
        }
    };
}

#endif
相关推荐
java1234_小锋1 小时前
Java高频面试题:RabbitMQ中有哪几种交换机类型?
java·rabbitmq·java-rabbitmq
空空潍2 小时前
RabbitMQ高级(2w字笔记)
java·rabbitmq·java-rabbitmq
爱学习的小可爱卢3 小时前
RabbitMQ—消息元数据解析指南
后端·rabbitmq
SoleMotive.1 天前
rabbitmq消息堆积怎么处理?
分布式·rabbitmq
聪明人1 天前
RabbitMQ HAProxy 负载均衡
rabbitmq·负载均衡·ruby
2401_848009722 天前
rabbitmq的高级知识-ttl与死信队列
分布式·rabbitmq·java-rabbitmq
luom01022 天前
使用 Docker 部署 RabbitMQ 的详细指南
docker·容器·rabbitmq
zzz84152 天前
集成RabbitMQ+MQ常用操作
分布式·rabbitmq
龙门吹雪2 天前
消息队列RabbitMQ的配置操作及使用
分布式·rabbitmq
Volunteer Technology2 天前
RabbitMQ面试场景题归纳
分布式·面试·rabbitmq