【消息队列项目】客户端四大模块实现

目录

一.订阅者模块

二.信道管理模块

2.1.信道类

2.2.信道关联类

2.3.代码整合

三.异步工作线程实现

四.连接管理模块


整体设计思路

在 RabbitMQ 的架构中,真正的服务提供者是信道(Channel),而非一个笼统的"客户端"概念。

因此,在设计客户端时,我们有意淡化了网络通信的细节,不直接向用户暴露 Client 对象,而是通过服务化的接口来体现功能。

其核心思想是:将每个操作封装为一个独立接口,内部实现完整的请求-响应流程,而对用户则表现为简单的本地方法调用------用户需要什么服务,就调用什么接口,无需关心背后的通信机制。

基于这一设计思想,客户端实现分为以下四大核心模块:


1. 订阅者模块

  • 角色:不直接面向用户,用于在内部描述"消费者"这一角色。

  • 这个和服务端的基本上是一样的,这个是对内的模块,不直接面向用户。

  • 作用:代表一个消息的接收端,负责维护消费行为相关的状态与回调,是实现消息订阅功能的基础载体。

2. 信道模块

  • 定位:直接面向用户的核心服务模块。

  • 功能:提供一系列封装好的服务接口,用户通过调用相应接口即可完成所需操作。主要包括:

    • 交换机的声明与删除

    • 队列的声明与删除

    • 绑定与解绑操作

    • 消息的发布与确认

    • 消息的订阅与取消订阅

  • 特点:接口设计力求直观,隐藏底层通信细节,让用户能够像调用本地 API 一样使用 RabbitMQ 的各项功能。

3. 连接模块

  • 定位:唯一直接体现网络通信概念的模块。

  • 职责:负责建立、维护与 RabbitMQ 服务端的 TCP 连接,并在此连接基础上创建信道。向用户提供的主要功能包括:

    • 建立连接(打开信道)

    • 关闭连接(释放资源)

  • 意义:作为信道的工厂,是客户端网络通信的起点与终点。

4. 异步线程模块

由于底层仍是网络通信,客户端必须具备事件监听与异步处理能力。本模块包含两个关键部分:

  • IO 事件监控线程:负责监听客户端连接上的 IO 事件(如可读、可写),及时触发相应的网络读写操作,保障通信的实时性。

  • 消息处理线程池:当信道作为消费者时,服务端会主动推送消息。为了不阻塞 IO 线程,并允许用户对消息进行灵活的业务处理,需要一个独立的工作线程池来异步执行这些消费逻辑。


客户端使用流程

基于以上模块,实现一个客户端的典型流程如下:

  1. 初始化异步线程模块,启动 IO 监听与消息处理线程池。

  2. 实例化连接对象,建立与 RabbitMQ 服务端的网络连接。

  3. 通过连接创建信道,获得一个可进行操作的 Channel 实例。

  4. 通过信道调用所需服务接口,如声明队列、发布消息或订阅消息。

  5. 使用完毕后,关闭信道,释放资源。

  6. 最后关闭连接,结束通信。

一.订阅者模块

与服务端相比,客户端中的订阅者模块存在感虽有所弱化,但其作用依然关键。

当通过信道订阅某个队列的消息时,内部便会隐式地创建一个订阅者(Subscriber)对象

该对象并非直接暴露给用户,而是作为承载消费行为信息与规则的核心载体,其主要作用包括:

  • 记录订阅关系:明确标识当前信道正在消费哪个队列的消息。

  • 定义处理逻辑:持有一个用户提供的回调函数,用于规定收到消息后应执行的具体业务处理流程。

  • 管理确认机制:通过一个标志位来控制消费确认模式,决定是否在消息处理后自动向服务器发送确认(Ack),或需用户手动确认。

一个订阅者对象通常包含以下核心信息:

  1. 订阅者标识 (Consumer Tag):一个唯一标识符,用于在连接范围内管理和引用此订阅者(例如,取消订阅时使用)。

  2. 目标队列名 (Queue Name):指定所订阅的源队列。

  3. 自动确认标志 (Auto Acknowledge Flag) :一个布尔值,决定消息的确认模式。若为true,则消息被交付后即视为自动确认;若为false,则需用户在业务处理完成后手动调用确认方法。

  4. 消息处理回调函数 (Message Handler Callback):一个可调用对象(如函数、仿函数等),其接口定义通常为处理单条消息。当信道收到消息时,最终会调用此函数,并将消息内容及相关属性作为参数传入。

通过封装这些信息,订阅者模块在背后将用户的消费意图与服务器的消息推送机制紧密衔接,使开发者能够专注于业务逻辑的实现,而无需处理底层的协议交互细节。

cpp 复制代码
#ifndef __M_CONSUMER_H__ 
#define __M_CONSUMER_H__ 

// 公共头文件包含
#include "../mqcommon/logger.hpp"     // 日志模块
#include "../mqcommon/helper.hpp"     // 工具函数模块
#include "../mqcommon/msg.pb.h"       // Protobuf消息定义

// 标准库头文件包含
#include <iostream> 
#include <unordered_map> 
#include <mutex> 
#include <memory> 
#include <vector> 
#include <functional> 

namespace mymq { 

// 消费者回调函数类型定义
// 参数说明:
// - const std::string: 消息体内容
// - const BasicProperties *bp: 消息属性指针
// - const std::string: 消息ID(或附加参数)
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;     // 消费者标识(Consumer Tag),唯一标识一个消费者
    std::string qname;   // 消费者订阅的队列名称(Queue Name)
    bool auto_ack;       // 自动确认标志(Auto Acknowledge Flag)
                         // true:自动确认,false:手动确认
    ConsumerCallback callback;  // 消息处理回调函数
    
    // 默认构造函数
    Consumer(){ 
        DLOG("new Consumer: %p", this);  // 调试日志:记录Consumer对象创建
    } 
    
    // 参数化构造函数
    // 参数说明:
    // - ctag: 消费者标识
    // - queue_name: 队列名称
    // - ack_flag: 自动确认标志
    // - cb: 消息处理回调函数
    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对象创建
    } 
    
    // 析构函数
    ~Consumer() { 
        DLOG("del Consumer: %p", this);  // 调试日志:记录Consumer对象销毁
    } 
}; 

} // namespace bitmq

#endif 

二.信道管理模块

2.1.信道类

在客户端设计中,信道(Channel)是提供服务的核心载体。

客户端信道与服务端信道在功能设计上基本一致,二者本质上都是为上层提供具体AMQP操作的服务接口。

区别在于:

  • 服务端信道用于响应并处理来自客户端的AMQP请求
  • 而客户端信道则面向最终用户,将其本地方法调用转换为对服务端的请求,从而隐藏了网络通信细节

用户可以简单理解为:通过调用客户端信道提供的接口,间接向服务端发送指令并获取相应的服务结果。

信道内部信息

每个信道对象维护着以下核心状态与资源,以确保其正常工作:

  1. 信道ID:在连接范围内唯一标识本信道的编号。

  2. 信道关联的TCP连接对象:指向所属网络连接的指针或引用,是所有网络通信的底层通道。

  3. Protobuf协议处理对象:用于序列化请求与反序列化响应的Protobuf编解码器。

  4. 信道关联的消费者集合:记录本信道上创建的所有订阅者(Consumer),管理消息的消费逻辑。

  5. 请求对应的响应信息队列 :这里队列使用 <请求ID为键,响应消息> 哈希表,以便于查找指定的响应 。由于采用异步网络通信模型(muduo库),请求发出后响应并非立即到达。此结构用于临时存储已到达的响应,以便在同步等待时能快速查找到指定请求的响应。

  6. **互斥锁&条件变量:**大部分的请求都是阻塞操作,发送请求后需要等到响应才 能继续,但是muduo库的通信是异步的,因此需要我们自己在收到响应后,通过 判断是否是等待的指定响应来进行同步

信道模块作为直接面向用户的核心服务单元,提供了一套完整的AMQP协议操作接口。这些接口的设计遵循同步调用、异步实现的原则:用户以同步方式调用接口,而内部则完成请求的构造、发送、等待响应及结果处理的全流程,将复杂的网络通信和协议交互完全封装。

信道对外服务接口

信道提供的服务主要包括以下六大类别:

  1. 信道生命周期管理

    • 创建新信道

    • 删除指定信道

  2. 交换机管理

    • 声明交换机:若指定名称的交换机已存在,则验证其参数是否匹配;若不存在,则按照给定参数创建新的交换机。也就是强断言-有则OK,没有则创建

    • 删除交换机

  3. 队列管理

    • 声明队列:若指定名称的队列已存在,则验证其参数;若不存在,则创建新队列。也就是强断言-有则OK,没有则创建

    • 删除队列

  4. 绑定关系管理

    • 将队列绑定到指定的交换机

    • 解除队列与交换机之间的绑定

  5. 消息消费管理

    • 订阅队列消息:为指定队列创建一个消费者,并定义消息到达时的处理逻辑。

    • 取消消息订阅:停止消费并移除指定的消费者。

  6. 消息生产与确认

    • 发布消息:将消息发布到指定的交换机。

    • 确认消息:在手动确认模式下,向服务器发送消息处理完成的确认信号。

我们很快就能写出下面这个模板

cpp 复制代码
// 类型别名定义
typedef std::shared_ptr<google::protobuf::Message> MessagePtr;                    // Protobuf消息智能指针
using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;                         // Protobuf编解码器智能指针
using basicConsumeResponsePtr = std::shared_ptr<basicConsumeResponse>;           // 消费响应智能指针
using basicCommonResponsePtr = std::shared_ptr<basicCommonResponse>;             // 通用响应智能指针

// 信道类 - AMQP操作的核心封装
class Channel { 
public: 
    using ptr = std::shared_ptr<Channel>;                                         // 信道智能指针类型别名

    // 构造函数
    // 参数说明:
    // - conn: TCP连接对象,用于底层网络通信
    // - codec: Protobuf编解码器,用于序列化/反序列化
    Channel(const muduo::net::TcpConnectionPtr& conn, const ProtobufCodecPtr& codec); 

    // 析构函数 - 自动取消订阅
    ~Channel(); 

    // 获取信道ID
    std::string cid(); 

    // 打开信道
    // 向服务器发送打开信道请求,并等待响应
    // 返回值:操作是否成功
    bool openChannel(); 

    // 关闭信道
    // 向服务器发送关闭信道请求,并等待响应
    void closeChannel(); 

    // 声明交换机
    // 参数说明:
    // - name: 交换机名称
    // - type: 交换机类型(直接、主题、扇出等)
    // - durable: 是否持久化
    // - auto_delete: 是否自动删除
    // - args: 附加参数(如消息TTL等)
    // 返回值:操作是否成功
    bool declareExchange( 
        const std::string &name, 
        ExchangeType type,  
        bool durable,  
        bool auto_delete, 
        google::protobuf::Map<std::string, std::string> &args); 

    // 删除交换机
    // 参数说明:
    // - name: 要删除的交换机名称
    void deleteExchange(const std::string &name); 

    // 声明队列
    // 参数说明:
    // - qname: 队列名称
    // - qdurable: 是否持久化
    // - qexclusive: 是否排他队列(仅当前连接可见)
    // - qauto_delete: 是否自动删除
    // - qargs: 附加参数
    // 返回值:操作是否成功
    bool declareQueue( 
        const std::string &qname,  
        bool qdurable,  
        bool qexclusive, 
        bool qauto_delete, 
        google::protobuf::Map<std::string, std::string> &qargs); 

    // 删除队列
    // 参数说明:
    // - qname: 要删除的队列名称
    void deleteQueue(const std::string &qname); 

    // 绑定队列到交换机
    // 参数说明:
    // - ename: 交换机名称
    // - qname: 队列名称
    // - key: 路由键
    // 返回值:操作是否成功
    bool queueBind( 
        const std::string &ename,  
        const std::string &qname,  
        const std::string &key); 

    // 解绑队列与交换机
    // 参数说明:
    // - ename: 交换机名称
    // - qname: 队列名称
    void queueUnBind(const std::string &ename, const std::string &qname); 

    // 发布消息
    // 参数说明:
    // - ename: 目标交换机名称
    // - bp: 消息属性(可为空指针)
    // - body: 消息体内容
    void basicPublish( 
        const std::string &ename, 
        const BasicProperties *bp, 
        const std::string &body); 

    // 确认消息(手动确认模式)
    // 参数说明:
    // - msgid: 要确认的消息ID
    void basicAck(const std::string &msgid); 

    // 取消订阅
    // 向服务器发送取消订阅请求,并清理本地消费者信息
    void basicCancel(); 

    // 订阅队列消息
    // 参数说明:
    // - consumer_tag: 消费者标识
    // - queue_name: 要订阅的队列名称
    // - auto_ack: 是否自动确认
    // - cb: 消息处理回调函数
    // 返回值:订阅是否成功
    bool basicConsume( 
        const std::string &consumer_tag, 
        const std::string &queue_name, 
        bool auto_ack, 
        const ConsumerCallback &cb); 

    // 添加通用响应到哈希表
    // 由网络模块在收到服务器响应时调用
    void putBasicResponse(const basicCommonResponsePtr& resp); 

    // 消费消息(处理服务器推送的消息)
    // 由网络模块在收到消息推送时调用
    void consume(const basicConsumeResponsePtr& resp); 

private: 
    // 等待指定请求的响应
    // 参数说明:
    // - rid: 请求ID
    // 返回值:服务器响应指针
    basicCommonResponsePtr waitResponse(const std::string &rid); 

private: 
    std::string _cid;                                                             // 信道ID
    muduo::net::TcpConnectionPtr _conn;                                           // TCP连接对象
    ProtobufCodecPtr _codec;                                                      // Protobuf编解码器
    Consumer::ptr _consumer;                                                      // 消费者对象(每个信道最多一个消费者)
    std::mutex _mutex;                                                            // 互斥锁,保护响应哈希表
    std::condition_variable _cv;                                                  // 条件变量,用于同步等待响应
    std::unordered_map<std::string, basicCommonResponsePtr> _basic_resp;          // 响应哈希表:请求ID -> 响应对象
}; 

我们很快就能写出下面这个

cpp 复制代码
// 类型别名定义
using MessagePtr = std::shared_ptr<google::protobuf::Message>;         // Protobuf消息智能指针
using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;               // Protobuf编解码器智能指针
using basicConsumeResponsePtr = std::shared_ptr<basicConsumeResponse>; // 消费响应智能指针
using basicCommonResponsePtr = std::shared_ptr<basicCommonResponse>;   // 通用响应智能指针

// 信道类 - AMQP操作的核心封装
class Channel
{
public:
    using ptr = std::shared_ptr<Channel>; // 信道智能指针类型别名

    // 构造函数
    // 参数说明:
    // - conn: TCP连接对象,用于底层网络通信
    // - codec: Protobuf编解码器,用于序列化/反序列化
    Channel(const muduo::net::TcpConnectionPtr &conn,
            const ProtobufCodecPtr &codec) : _cid(UUIDHelper::uuid()), // 生成唯一信道ID
                                             _conn(conn),              // TCP连接对象
                                             _codec(codec)             // Protobuf协议解析类
    {
    }

    // 析构函数 - 自动取消订阅
    ~Channel()
    {
        basicCancel(); // 向服务器发送取消订阅请求,并清理本地消费者信息
    }

    // 获取信道ID
    std::string cid()
    {
        return _cid;
    }

    // 打开信道
    // 向服务器发送打开信道请求,并等待响应
    // 返回值:操作是否成功
    bool openChannel()
    {
        std::string rid = UUIDHelper::uuid(); // 生成唯一请求ID
        openChannelRequest req;               // 创建信道请求对象------注意是proto.proto文件里定义的
        req.set_rid(rid);                     // 设置请求唯一ID
        req.set_cid(_cid);                    // 设置信道ID
        _codec->send(_conn, req);             // 发送请求
        // basicCommonResponsePtr是proto.proto文件里定义的通用响应
        basicCommonResponsePtr resp = waitResponse(rid); // 因为Muduo库内部的网络通信是异步操作,所以需要等待服务器响应
        return resp->ok();                               // 通用响应里面的ok代表本次打开信道操作是否成功
    }

    // 关闭信道
    // 向服务器发送关闭信道请求,并等待响应
    void closeChannel()
    {
        // 1.构建请求对象,往里面填充字段
        std::string rid = UUIDHelper::uuid(); // 生成唯一请求ID
        closeChannelRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        // 2.往服务器发送请求
        _codec->send(_conn, req);
        // 3.等待服务器返回相应
        waitResponse(rid); // 等待响应
        return;
    }

    // 声明交换机
    // 参数说明:
    // - name: 交换机名称
    // - type: 交换机类型(直接、主题、扇出等)
    // - durable: 是否持久化
    // - auto_delete: 是否自动删除
    // - args: 附加参数(如消息TTL等)
    // 返回值:操作是否成功
    bool declareExchange(
        const std::string &name,
        ExchangeType type,
        bool durable,
        bool auto_delete,
        google::protobuf::Map<std::string, std::string> &args)
    {
        // 1.构建请求对象,往里面填充字段
        std::string rid = UUIDHelper::uuid();
        declareExchangeRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_exchange_name(name);
        req.set_exchange_type(type);
        req.set_durable(durable);
        req.set_auto_delete(auto_delete);
        req.mutable_args()->swap(args); // 交换参数,避免拷贝
        // 2.往服务器发送请求
        _codec->send(_conn, req);
        // 3.等待服务器返回相应
        basicCommonResponsePtr resp = waitResponse(rid);
        return resp->ok();
    }

    // 删除交换机
    // 参数说明:
    // - name: 要删除的交换机名称
    void deleteExchange(const std::string &name)
    {
        std::string rid = UUIDHelper::uuid();
        deleteExchangeRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_exchange_name(name);
        _codec->send(_conn, req);
        waitResponse(rid);
        return;
    }

    // 声明队列
    // 参数说明:
    // - qname: 队列名称
    // - qdurable: 是否持久化
    // - qexclusive: 是否排他队列(仅当前连接可见)
    // - qauto_delete: 是否自动删除
    // - qargs: 附加参数
    // 返回值:操作是否成功
    bool declareQueue(
        const std::string &qname,
        bool qdurable,
        bool qexclusive,
        bool qauto_delete,
        google::protobuf::Map<std::string, std::string> &qargs)
    {

        std::string rid = UUIDHelper::uuid();
        declareQueueRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_queue_name(qname);
        req.set_durable(qdurable);
        req.set_auto_delete(qauto_delete);
        req.set_exclusive(qexclusive);
        req.mutable_args()->swap(qargs);

        _codec->send(_conn, req);

        basicCommonResponsePtr resp = waitResponse(rid);
        return resp->ok();
    }

    // 删除队列
    // 参数说明:
    // - qname: 要删除的队列名称
    void deleteQueue(const std::string &qname)
    {
        std::string rid = UUIDHelper::uuid();
        deleteQueueRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_queue_name(qname);

        _codec->send(_conn, req);

        waitResponse(rid);
        return;
    }

    // 绑定队列到交换机
    // 参数说明:
    // - ename: 交换机名称
    // - qname: 队列名称
    // - key: 路由键
    // 返回值:操作是否成功
    bool queueBind(
        const std::string &ename,
        const std::string &qname,
        const std::string &key)
    {

        std::string rid = UUIDHelper::uuid();
        queueBindRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_exchange_name(ename);
        req.set_queue_name(qname);
        req.set_binding_key(key);

        _codec->send(_conn, req);

        basicCommonResponsePtr resp = waitResponse(rid);
        return resp->ok();
    }

    // 解绑队列与交换机
    // 参数说明:
    // - ename: 交换机名称
    // - qname: 队列名称
    void queueUnBind(const std::string &ename, const std::string &qname)
    {
        std::string rid = UUIDHelper::uuid();
        queueUnBindRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_exchange_name(ename);
        req.set_queue_name(qname);

        _codec->send(_conn, req);

        waitResponse(rid);
        return;
    }

    // 发布消息
    // 参数说明:
    // - ename: 目标交换机名称
    // - bp: 消息属性(可为空指针)
    // - body: 消息体内容
    void basicPublish(
        const std::string &ename,
        const BasicProperties *bp,
        const std::string &body)
    {

        std::string rid = UUIDHelper::uuid(); // 生成唯一的消息请求ID
        basicPublishRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_body(body);
        req.set_exchange_name(ename);

        // 如果消息属性不为空,设置属性
        if (bp != nullptr)
        {
            req.mutable_properties()->set_id(bp->id()); // mutable_properties()返回的是可写的指针
            req.mutable_properties()->set_delivery_mode(bp->delivery_mode());
            req.mutable_properties()->set_routing_key(bp->routing_key());
        }
        _codec->send(_conn, req);
        waitResponse(rid);
        return;
    }

    // 确认消息(手动确认模式)
    // 参数说明:
    // - msgid: 要确认的消息ID
    void basicAck(const std::string &msgid)
    {
        if (_consumer.get() == nullptr)
        {
            DLOG("消息确认时,找不到消费者信息!");
            return;
        }

        std::string rid = UUIDHelper::uuid();
        basicAckRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_queue_name(_consumer->qname);
        req.set_message_id(msgid);

        _codec->send(_conn, req);

        waitResponse(rid);
        return;
    }

    // 取消订阅
    // 向服务器发送取消订阅请求,并清理本地消费者信息
    void basicCancel()
    {
        if (_consumer.get() == nullptr)
        {
            return;
        }
        std::string rid = UUIDHelper::uuid();
        basicCancelRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_queue_name(_consumer->qname);
        req.set_consumer_tag(_consumer->tag);
        _codec->send(_conn, req);
        waitResponse(rid);
        _consumer.reset(); // 清理消费者信息
        return;
    }

    // 订阅队列消息
    // 参数说明:
    // - consumer_tag: 消费者标识
    // - queue_name: 要订阅的队列名称
    // - auto_ack: 是否自动确认
    // - cb: 消息处理回调函数
    // 返回值:订阅是否成功
    bool basicConsume(
        const std::string &consumer_tag,
        const std::string &queue_name,
        bool auto_ack,
        const ConsumerCallback &cb)
    {

        if (_consumer.get() != nullptr)
        {
            DLOG("当前信道已订阅其他队列消息!");
            return false;
        }
        std::string rid = UUIDHelper::uuid();
        basicConsumeRequest req;
        req.set_rid(rid);
        req.set_cid(_cid);
        req.set_queue_name(queue_name);
        req.set_consumer_tag(consumer_tag);
        req.set_auto_ack(auto_ack);

        _codec->send(_conn, req);

        basicCommonResponsePtr resp = waitResponse(rid);
        if (resp->ok() == false)
        {
            DLOG("添加订阅失败!");
            return false;
        }

        // 创建消费者对象并保存
        _consumer = std::make_shared<Consumer>(consumer_tag, queue_name, auto_ack, cb);
        return true;
    }

public:
    // 添加通用响应到哈希表
    // 由网络模块在收到服务器响应时调用
    void putBasicResponse(const basicCommonResponsePtr &resp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _basic_resp.insert(std::make_pair(resp->rid(), resp)); // 按请求ID存储响应
        _cv.notify_all();                                      // 通知等待线程
    }

    // 消费消息(处理服务器推送的消息)
    // 由网络模块在收到消息推送时调用
    void consume(const basicConsumeResponsePtr &resp)
    {
        if (_consumer.get() == nullptr)
        {
            DLOG("消息处理时,未找到订阅者信息!");
            return;
        }
        if (_consumer->tag != resp->consumer_tag())
        {
            DLOG("收到的推送消息中的消费者标识,与当前信道消费者标识不一致!");
            return;
        }
        // 调用用户注册的回调函数处理消息
        _consumer->callback(resp->consumer_tag(), resp->mutable_properties(), resp->body());
    }

private:
    // 等待指定请求的响应
    // 参数说明:
    // - rid: 请求ID
    // 返回值:服务器响应指针
    basicCommonResponsePtr waitResponse(const std::string &rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);

        // 条件等待:直到对应请求ID的响应到达
        _cv.wait(lock, [&rid, this]()
                 { return _basic_resp.find(rid) != _basic_resp.end(); });

        // 获取响应并从响应哈希表中移除
        basicCommonResponsePtr basic_resp = _basic_resp[rid];
        _basic_resp.erase(rid);
        return basic_resp;
    }

private:
    std::string _cid;                                                    // 信道ID
    muduo::net::TcpConnectionPtr _conn;                                  // TCP连接对象
    ProtobufCodecPtr _codec;                                             // Protobuf编解码器
    Consumer::ptr _consumer;                                             // 消费者对象(每个信道最多一个消费者)
    std::mutex _mutex;                                                   // 互斥锁,保护响应哈希表
    std::condition_variable _cv;                                         // 条件变量,用于同步等待响应
    std::unordered_map<std::string, basicCommonResponsePtr> _basic_resp; // 响应哈希表:请求ID -> 响应对象
};

这个其实是很简单的

2.2.信道关联类

作为信道的容器与工厂,信道管理器负责信道的统一生命周期管理,主要提供以下功能:

  • 创建信道:根据连接和指定的信道ID,创建并登记一个新的信道实例。

  • 查找信道:通过信道ID快速检索到对应的信道对象。

  • 销毁信道:安全地关闭并清理指定信道,释放其所有关联资源。

cpp 复制代码
// 信道管理器类 - 管理所有信道的生命周期
class ChannelManager
{
public:
    using ptr = std::shared_ptr<ChannelManager>; // 管理器智能指针类型别名

    ChannelManager() {}

    // 创建信道
    // 参数说明:
    // - conn: TCP连接对象
    // - codec: Protobuf编解码器
    // 返回值:新创建的信道指针
    Channel::ptr create(const muduo::net::TcpConnectionPtr &conn,
                        const ProtobufCodecPtr &codec)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto channel = std::make_shared<Channel>(conn, codec);     // 创建信道对象
        _channels.insert(std::make_pair(channel->cid(), channel)); // 按信道ID存储
        return channel;
    }

    // 删除信道
    // 参数说明:
    // - cid: 要删除的信道ID
    void remove(const std::string &cid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _channels.erase(cid);
    }

    // 获取信道
    // 参数说明:
    // - cid: 要获取的信道ID
    // 返回值:信道指针,如果不存在则返回空指针
    Channel::ptr get(const std::string &cid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _channels.find(cid);
        if (it == _channels.end())
        {
            return Channel::ptr();
        }
        return it->second;
    }

private:
    std::mutex _mutex;                                       // 互斥锁,保护信道哈希表
    std::unordered_map<std::string, Channel::ptr> _channels; // 信道哈希表:信道ID -> 信道对象
};

2.3.代码整合

复制代码
#ifndef __M_CHANNEL_H__
#define __M_CHANNEL_H__

// 网络通信相关
#include "../third/include/muduo/net/TcpConnection.h"
#include "../third/include/muduo/protobuf/codec.h"
#include "../third/include/muduo/protobuf/dispatcher.h"

// 项目公共模块
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"
#include "../mqcommon/msg.pb.h"
#include "../mqcommon/proto.pb.h"

// 订阅者模块
#include "consumer.hpp"

// 标准库
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <unordered_map>
#include <functional>

namespace mymq
{

    // 类型别名定义
    using MessagePtr = std::shared_ptr<google::protobuf::Message>;         // Protobuf消息智能指针
    using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;               // Protobuf编解码器智能指针
    using basicConsumeResponsePtr = std::shared_ptr<basicConsumeResponse>; // 消费响应智能指针
    using basicCommonResponsePtr = std::shared_ptr<basicCommonResponse>;   // 通用响应智能指针

    // 信道类 - AMQP操作的核心封装
    class Channel
    {
    public:
        using ptr = std::shared_ptr<Channel>; // 信道智能指针类型别名

        // 构造函数
        // 参数说明:
        // - conn: TCP连接对象,用于底层网络通信
        // - codec: Protobuf编解码器,用于序列化/反序列化
        Channel(const muduo::net::TcpConnectionPtr &conn,
                const ProtobufCodecPtr &codec) : _cid(UUIDHelper::uuid()), // 生成唯一信道ID
                                                 _conn(conn),              // TCP连接对象
                                                 _codec(codec)             // Protobuf协议解析类
        {
        }

        // 析构函数 - 自动取消订阅
        ~Channel()
        {
            basicCancel(); // 向服务器发送取消订阅请求,并清理本地消费者信息
        }

        // 获取信道ID
        std::string cid()
        {
            return _cid;
        }

        // 打开信道
        // 向服务器发送打开信道请求,并等待响应
        // 返回值:操作是否成功
        bool openChannel()
        {
            std::string rid = UUIDHelper::uuid(); // 生成唯一请求ID
            openChannelRequest req;               // 创建信道请求对象------注意是proto.proto文件里定义的
            req.set_rid(rid);                     // 设置请求唯一ID
            req.set_cid(_cid);                    // 设置信道ID
            _codec->send(_conn, req);             // 发送请求
            // basicCommonResponsePtr是proto.proto文件里定义的通用响应
            basicCommonResponsePtr resp = waitResponse(rid); // 因为Muduo库内部的网络通信是异步操作,所以需要等待服务器响应
            return resp->ok();                               // 通用响应里面的ok代表本次打开信道操作是否成功
        }

        // 关闭信道
        // 向服务器发送关闭信道请求,并等待响应
        void closeChannel()
        {
            // 1.构建请求对象,往里面填充字段
            std::string rid = UUIDHelper::uuid(); // 生成唯一请求ID
            closeChannelRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            // 2.往服务器发送请求
            _codec->send(_conn, req);
            // 3.等待服务器返回相应
            waitResponse(rid); // 等待响应
            return;
        }

        // 声明交换机
        // 参数说明:
        // - name: 交换机名称
        // - type: 交换机类型(直接、主题、扇出等)
        // - durable: 是否持久化
        // - auto_delete: 是否自动删除
        // - args: 附加参数(如消息TTL等)
        // 返回值:操作是否成功
        bool declareExchange(
            const std::string &name,
            ExchangeType type,
            bool durable,
            bool auto_delete,
            google::protobuf::Map<std::string, std::string> &args)
        {
            // 1.构建请求对象,往里面填充字段
            std::string rid = UUIDHelper::uuid();
            declareExchangeRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_exchange_name(name);
            req.set_exchange_type(type);
            req.set_durable(durable);
            req.set_auto_delete(auto_delete);
            req.mutable_args()->swap(args); // 交换参数,避免拷贝
            // 2.往服务器发送请求
            _codec->send(_conn, req);
            // 3.等待服务器返回相应
            basicCommonResponsePtr resp = waitResponse(rid);
            return resp->ok();
        }

        // 删除交换机
        // 参数说明:
        // - name: 要删除的交换机名称
        void deleteExchange(const std::string &name)
        {
            std::string rid = UUIDHelper::uuid();
            deleteExchangeRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_exchange_name(name);
            _codec->send(_conn, req);
            waitResponse(rid);
            return;
        }

        // 声明队列
        // 参数说明:
        // - qname: 队列名称
        // - qdurable: 是否持久化
        // - qexclusive: 是否排他队列(仅当前连接可见)
        // - qauto_delete: 是否自动删除
        // - qargs: 附加参数
        // 返回值:操作是否成功
        bool declareQueue(
            const std::string &qname,
            bool qdurable,
            bool qexclusive,
            bool qauto_delete,
            google::protobuf::Map<std::string, std::string> &qargs)
        {

            std::string rid = UUIDHelper::uuid();
            declareQueueRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_queue_name(qname);
            req.set_durable(qdurable);
            req.set_auto_delete(qauto_delete);
            req.set_exclusive(qexclusive);
            req.mutable_args()->swap(qargs);

            _codec->send(_conn, req);

            basicCommonResponsePtr resp = waitResponse(rid);
            return resp->ok();
        }

        // 删除队列
        // 参数说明:
        // - qname: 要删除的队列名称
        void deleteQueue(const std::string &qname)
        {
            std::string rid = UUIDHelper::uuid();
            deleteQueueRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_queue_name(qname);

            _codec->send(_conn, req);

            waitResponse(rid);
            return;
        }

        // 绑定队列到交换机
        // 参数说明:
        // - ename: 交换机名称
        // - qname: 队列名称
        // - key: 路由键
        // 返回值:操作是否成功
        bool queueBind(
            const std::string &ename,
            const std::string &qname,
            const std::string &key)
        {

            std::string rid = UUIDHelper::uuid();
            queueBindRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_exchange_name(ename);
            req.set_queue_name(qname);
            req.set_binding_key(key);

            _codec->send(_conn, req);

            basicCommonResponsePtr resp = waitResponse(rid);
            return resp->ok();
        }

        // 解绑队列与交换机
        // 参数说明:
        // - ename: 交换机名称
        // - qname: 队列名称
        void queueUnBind(const std::string &ename, const std::string &qname)
        {
            std::string rid = UUIDHelper::uuid();
            queueUnBindRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_exchange_name(ename);
            req.set_queue_name(qname);

            _codec->send(_conn, req);

            waitResponse(rid);
            return;
        }

        // 发布消息
        // 参数说明:
        // - ename: 目标交换机名称
        // - bp: 消息属性(可为空指针)
        // - body: 消息体内容
        void basicPublish(
            const std::string &ename,
            const BasicProperties *bp,
            const std::string &body)
        {

            std::string rid = UUIDHelper::uuid(); // 生成唯一的消息请求ID
            basicPublishRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_body(body);
            req.set_exchange_name(ename);

            // 如果消息属性不为空,设置属性
            if (bp != nullptr)
            {
                req.mutable_properties()->set_id(bp->id()); // mutable_properties()返回的是可写的指针
                req.mutable_properties()->set_delivery_mode(bp->delivery_mode());
                req.mutable_properties()->set_routing_key(bp->routing_key());
            }
            _codec->send(_conn, req);
            waitResponse(rid);
            return;
        }

        // 确认消息(手动确认模式)
        // 参数说明:
        // - msgid: 要确认的消息ID
        void basicAck(const std::string &msgid)
        {
            if (_consumer.get() == nullptr)
            {
                DLOG("消息确认时,找不到消费者信息!");
                return;
            }

            std::string rid = UUIDHelper::uuid();
            basicAckRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_queue_name(_consumer->qname);
            req.set_message_id(msgid);

            _codec->send(_conn, req);

            waitResponse(rid);
            return;
        }

        // 取消订阅
        // 向服务器发送取消订阅请求,并清理本地消费者信息
        void basicCancel()
        {
            if (_consumer.get() == nullptr)
            {
                return;
            }
            std::string rid = UUIDHelper::uuid();
            basicCancelRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_queue_name(_consumer->qname);
            req.set_consumer_tag(_consumer->tag);
            _codec->send(_conn, req);
            waitResponse(rid);
            _consumer.reset(); // 清理消费者信息
            return;
        }

        // 订阅队列消息
        // 参数说明:
        // - consumer_tag: 消费者标识
        // - queue_name: 要订阅的队列名称
        // - auto_ack: 是否自动确认
        // - cb: 消息处理回调函数
        // 返回值:订阅是否成功
        bool basicConsume(
            const std::string &consumer_tag,
            const std::string &queue_name,
            bool auto_ack,
            const ConsumerCallback &cb)
        {

            if (_consumer.get() != nullptr)
            {
                DLOG("当前信道已订阅其他队列消息!");
                return false;
            }
            std::string rid = UUIDHelper::uuid();
            basicConsumeRequest req;
            req.set_rid(rid);
            req.set_cid(_cid);
            req.set_queue_name(queue_name);
            req.set_consumer_tag(consumer_tag);
            req.set_auto_ack(auto_ack);

            _codec->send(_conn, req);

            basicCommonResponsePtr resp = waitResponse(rid);
            if (resp->ok() == false)
            {
                DLOG("添加订阅失败!");
                return false;
            }

            // 创建消费者对象并保存
            _consumer = std::make_shared<Consumer>(consumer_tag, queue_name, auto_ack, cb);
            return true;
        }

    public:
        // 添加通用响应到哈希表
        // 由网络模块在收到服务器响应时调用
        void putBasicResponse(const basicCommonResponsePtr &resp)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _basic_resp.insert(std::make_pair(resp->rid(), resp)); // 按请求ID存储响应
            _cv.notify_all();                                      // 通知等待线程
        }

        // 消费消息(处理服务器推送的消息)
        // 由网络模块在收到消息推送时调用
        void consume(const basicConsumeResponsePtr &resp)
        {
            if (_consumer.get() == nullptr)
            {
                DLOG("消息处理时,未找到订阅者信息!");
                return;
            }
            if (_consumer->tag != resp->consumer_tag())
            {
                DLOG("收到的推送消息中的消费者标识,与当前信道消费者标识不一致!");
                return;
            }
            // 调用用户注册的回调函数处理消息
            _consumer->callback(resp->consumer_tag(), resp->mutable_properties(), resp->body());
        }

    private:
        // 等待指定请求的响应
        // 参数说明:
        // - rid: 请求ID
        // 返回值:服务器响应指针
        basicCommonResponsePtr waitResponse(const std::string &rid)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // 条件等待:直到对应请求ID的响应到达
            _cv.wait(lock, [&rid, this]()
                     { return _basic_resp.find(rid) != _basic_resp.end(); });

            // 获取响应并从响应哈希表中移除
            basicCommonResponsePtr basic_resp = _basic_resp[rid];
            _basic_resp.erase(rid);
            return basic_resp;
        }

    private:
        std::string _cid;                                                    // 信道ID
        muduo::net::TcpConnectionPtr _conn;                                  // TCP连接对象
        ProtobufCodecPtr _codec;                                             // Protobuf编解码器
        Consumer::ptr _consumer;                                             // 消费者对象(每个信道最多一个消费者)
        std::mutex _mutex;                                                   // 互斥锁,保护响应哈希表
        std::condition_variable _cv;                                         // 条件变量,用于同步等待响应
        std::unordered_map<std::string, basicCommonResponsePtr> _basic_resp; // 响应哈希表:请求ID -> 响应对象
    };

    // 信道管理器类 - 管理所有信道的生命周期
    class ChannelManager
    {
    public:
        using ptr = std::shared_ptr<ChannelManager>; // 管理器智能指针类型别名

        ChannelManager() {}

        // 创建信道
        // 参数说明:
        // - conn: TCP连接对象
        // - codec: Protobuf编解码器
        // 返回值:新创建的信道指针
        Channel::ptr create(const muduo::net::TcpConnectionPtr &conn,
                            const ProtobufCodecPtr &codec)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto channel = std::make_shared<Channel>(conn, codec);     // 创建信道对象
            _channels.insert(std::make_pair(channel->cid(), channel)); // 按信道ID存储
            return channel;
        }

        // 删除信道
        // 参数说明:
        // - cid: 要删除的信道ID
        void remove(const std::string &cid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _channels.erase(cid);
        }

        // 获取信道
        // 参数说明:
        // - cid: 要获取的信道ID
        // 返回值:信道指针,如果不存在则返回空指针
        Channel::ptr get(const std::string &cid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _channels.find(cid);
            if (it == _channels.end())
            {
                return Channel::ptr();
            }
            return it->second;
        }

    private:
        std::mutex _mutex;                                       // 互斥锁,保护信道哈希表
        std::unordered_map<std::string, Channel::ptr> _channels; // 信道哈希表:信道ID -> 信道对象
    };
}
#endif // __M_CHANNEL_H__

三.异步工作线程实现

在客户端设计中,我们采用了两类异步工作线程来提升并发处理能力与系统响应效率:

  • 基于muduo库的客户端连接异步循环线程(EventLoopThread):负责管理网络I/O事件循环,处理连接的建立、数据收发等异步操作。该线程并非针对单一连接创建,而是作为一个共享的事件驱动循环,服务于多个客户端连接,有效减少线程资源开销并保持高并发性能。

  • 消息异步处理工作线程池:在接收到消息后,通过线程池进行异步任务处理,避免阻塞网络I/O线程。该线程池同样为多连接共享,提供可扩展的并发处理能力,确保业务逻辑在后台高效执行,进一步提升系统整体的吞吐量与响应速度。

注意:**这两类线程都是与连接无关的,即它们不是某个连接独享的。**一个I/O线程可以处理多个连接的网络I/O,一个工作线程池可以处理来自多个连接的消息任务。

下面是一个简单的流程:

  1. 客户端有多个连接,每个连接对应一个socket。

  2. 这些socket都被注册到同一个EventLoop(即EventLoopThread)中,由这个EventLoop来监听这些socket上的事件。

  3. 当某个socket可读时,EventLoop线程会读取数据,并将其组装成消息。

  4. 然后将消息封装成任务,提交给工作线程池。

  5. 工作线程池中的某个线程会取出任务并处理。

  6. 处理完成后,如果需要响应,则可以将响应消息通过EventLoop线程发送(因为socket的写操作通常也由EventLoop线程负责,以避免多线程同时写一个socket)。

因此,我们需要对他们进行单独封装。

cpp 复制代码
#ifndef __M_WORKER_H__ 
#define __M_WORKER_H__ 

#include "../third/include/muduo/net/EventLoopThread.h"  // Muduo网络库的事件循环线程
#include "../mqcommon/logger.hpp"    // 日志模块
#include "../mqcommon/helper.hpp"    // 工具函数
#include "../mqcommon/threadpool.hpp" // 线程池模块

namespace mymq { 
/**
 * @class AsyncWorker
 * @brief 异步工作器类,整合网络I/O线程和工作线程池
 * 
 * 这个类将两种异步工作线程封装在一起:
 * 1. EventLoopThread: 负责网络I/O的异步事件循环
 * 2. threadpool: 负责业务逻辑的异步处理
 * 
 * 设计特点:
 * - 两种线程资源都与具体连接解耦
 * - 多个连接共享这些线程资源
 * - 实现网络I/O与业务处理的分离
 */
class AsyncWorker { 
public: 
    using ptr = std::shared_ptr<AsyncWorker>; ///< 智能指针别名,便于资源管理
    
    muduo::net::EventLoopThread loopthread; ///< 网络I/O线程,处理连接的建立、数据收发等事件循环
    ThreadPool pool; ///< 工作线程池,负责处理接收到的消息和业务逻辑
    
    // 注意:这个类目前只是简单组合了两个线程组件
    // 在实际使用中,可能需要添加初始化、启动、停止等方法
};

} // namespace bitmq

#endif // __M_WORKER_H__

四.连接管理模块

连接管理模块主要负责客户端与RabbitMQ服务器之间的通信链路维护。

在RabbitMQ的设计中,客户端本身的概念被弱化,实际的服务调用均通过信道(Channel) 完成。

因此,整体操作流程可归纳为:

  1. 首先建立与RabbitMQ服务器的TCP连接;

  2. 基于该连接创建一个或多个信道;

  3. 通过信道进行具体的消息发布、消费等业务操作。

本模块是基于 muduo 网络库的客户端连接功能进行的二次封装,主要目标是向用户提供简洁的信道创建接口。用户成功创建信道后,即可通过该信道调用 RabbitMQ 的各项服务功能,如声明队列、发送消息、订阅消息等。

cpp 复制代码
#ifndef __M_CONNECTION_H__ 
#define __M_CONNECTION_H__ 

// 基础库包含
#include "muduo/proto/dispatcher.h" 
#include "muduo/proto/codec.h" 
#include "muduo/base/Logging.h" 
#include "muduo/base/Mutex.h" 
#include "muduo/net/EventLoop.h" 
#include "muduo/net/TcpClient.h" 
#include "muduo/net/EventLoopThread.h" 
#include "muduo/base/CountDownLatch.h" 

// 项目内部头文件
#include "mq_channel.hpp" 
#include "mq_worker.hpp" 

namespace bitmq { 

// RabbitMQ连接管理类,负责管理到RabbitMQ服务器的TCP连接和信道创建
class Connection { 
public: 
    using ptr = std::shared_ptr<Connection>; 

    // 构造函数:建立与RabbitMQ服务器的连接
    // sip: 服务器IP地址
    // sport: 服务器端口
    // worker: 异步工作器指针,包含事件循环和线程池
    Connection(const std::string &sip, int sport, const AsyncWorker::ptr &worker);

    // 打开一个新信道
    // 返回值: 成功返回信道指针,失败返回空指针
    Channel::ptr openChannel();

    // 关闭指定信道
    // channel: 要关闭的信道指针
    void closeChannel(const Channel::ptr &channel);

private: 
    // 处理基础响应消息
    // conn: TCP连接指针
    // message: 接收到的响应消息
    // timestamp: 时间戳
    void basicResponse(const muduo::net::TcpConnectionPtr& conn,  
                      const basicCommonResponsePtr& message,
                      muduo::Timestamp);

    // 处理消费响应消息
    // conn: TCP连接指针
    // message: 接收到的消费响应消息
    // timestamp: 时间戳
    void consumeResponse(const muduo::net::TcpConnectionPtr& conn,  
                        const basicConsumeResponsePtr& message,
                        muduo::Timestamp);

    // 处理未知消息类型
    // conn: TCP连接指针
    // message: 未知消息
    // timestamp: 时间戳
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn,  
                         const MessagePtr& message, 
                         muduo::Timestamp);

    // TCP连接状态变化回调
    // conn: TCP连接指针
    void onConnection(const muduo::net::TcpConnectionPtr& conn);

private: 
    muduo::CountDownLatch _latch;          // 同步原语,用于等待连接建立
    muduo::net::TcpConnectionPtr _conn;    // 客户端对应的TCP连接指针
    muduo::net::TcpClient _client;         // Muduo TCP客户端对象
    ProtobufDispatcher _dispatcher;        // 协议消息分发器
    ProtobufCodecPtr _codec;               // 协议编解码器指针
    AsyncWorker::ptr _worker;              // 异步工作器指针
    ChannelManager::ptr _channel_manager;  // 信道管理器指针
}; 

} // namespace bitmq

#endif

这个其实和我们之前写的那个是一样的

cpp 复制代码
#ifndef __M_CONNECTION_H__ 
#define __M_CONNECTION_H__ 

// 基础库包含
#include "../third/include/muduo/protobuf/dispatcher.h" 
#include "../third/include/muduo/protobuf/codec.h" 
#include "../third/include/muduo/base/Logging.h" 
#include "../third/include/muduo/base/Mutex.h" 
#include "../third/include/muduo/net/EventLoop.h" 
#include "../third/include/muduo/net/TcpClient.h" 
#include "../third/include/muduo/net/EventLoopThread.h" 
#include "../third/include/muduo/base/CountDownLatch.h" 

// 项目内部头文件
#include "channel.hpp" 
#include "worker.hpp" 

namespace mymq { 

// RabbitMQ连接管理类,负责管理到RabbitMQ服务器的TCP连接和信道创建
class Connection { 
public: 
    using ptr = std::shared_ptr<Connection>; 

    // 构造函数:建立与RabbitMQ服务器的连接
    // sip: 服务器IP地址
    // sport: 服务器端口
    // worker: 异步工作器指针,包含事件循环和线程池
    Connection(const std::string &sip, int sport, const AsyncWorker::ptr &worker): 
        _latch(1), // 初始化CountDownLatch为1,用于等待连接建立
        _client(worker->loopthread.startLoop(),  // 使用工作器的事件循环
                muduo::net::InetAddress(sip, sport), // 服务器地址
                "Client"), // 客户端名称
        _dispatcher(std::bind(&Connection::onUnknownMessage, // 未知消息回调
                              this,  
                              std::placeholders::_1, 
                              std::placeholders::_2,
                              std::placeholders::_3)),
        _codec(std::make_shared<ProtobufCodec>(  // 创建协议编解码器
                 std::bind(&ProtobufDispatcher::onProtobufMessage,
                           &_dispatcher,  
                           std::placeholders::_1, 
                           std::placeholders::_2,
                           std::placeholders::_3))),
        _worker(worker), // 保存工作器指针
        _channel_manager(std::make_shared<ChannelManager>()) // 创建信道管理器
    { 
        // 注册基础响应消息的回调处理
        _dispatcher.registerMessageCallback<mymq::basicCommonResponse>(
            std::bind(&Connection::basicResponse, this,  
                      std::placeholders::_1, 
                      std::placeholders::_2,
                      std::placeholders::_3));
        
        // 注册消费响应消息的回调处理
        _dispatcher.registerMessageCallback<mymq::basicConsumeResponse>(
            std::bind(&Connection::consumeResponse, this,  
                      std::placeholders::_1, 
                      std::placeholders::_2,
                      std::placeholders::_3));
        
        // 设置TCP客户端的消息回调,将接收到的数据交给编解码器处理
        _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage,
                                             _codec.get(),
                                             std::placeholders::_1, 
                                             std::placeholders::_2,
                                             std::placeholders::_3));
        
        // 设置TCP客户端的连接状态变化回调
        _client.setConnectionCallback(std::bind(&Connection::onConnection, 
                                                this, 
                                                std::placeholders::_1));
        
        // 发起TCP连接
        _client.connect(); 
        
        // 阻塞等待,直到连接建立成功
        _latch.wait();
    } 

    // 打开一个新信道
    // 返回值: 成功返回信道指针,失败返回空指针
    Channel::ptr openChannel() { 
        // 通过信道管理器创建新信道
        Channel::ptr channel = _channel_manager->create(_conn, _codec); //客户端自己创建一个信道
        
        // 尝试打开信道
        bool ret = channel->openChannel(); //往服务器发送一个信道创建请求
        if (ret == false) { 
            DLOG("打开信道失败!"); 
            return Channel::ptr(); // 返回空指针
        } 
        
        return channel; // 返回信道指针
    } 

    // 关闭指定信道
    // channel: 要关闭的信道指针
    void closeChannel(const Channel::ptr &channel) { 
        channel->closeChannel(); // 关闭信道------向服务器发送关闭信道请求
        _channel_manager->remove(channel->cid()); // 从管理器移除
    } 

private: 
    // 处理基础响应消息
    // conn: TCP连接指针
    // message: 接收到的响应消息
    // timestamp: 时间戳
    void basicResponse(const muduo::net::TcpConnectionPtr& conn,  
                      const basicCommonResponsePtr& message,
                      muduo::Timestamp) { 
        // 1. 根据信道ID找到对应的信道
        Channel::ptr channel = _channel_manager->get(message->cid());
        if (channel.get() == nullptr) { 
            DLOG("未找到信道信息!"); 
            return; 
        } 
        
        // 2. 将得到的响应对象,添加到信道的基础响应映射表中
        channel->putBasicResponse(message); 
    } 

    // 处理消费响应消息
    // conn: TCP连接指针
    // message: 接收到的消费响应消息
    // timestamp: 时间戳
    void consumeResponse(const muduo::net::TcpConnectionPtr& conn,  
                        const basicConsumeResponsePtr& message,
                        muduo::Timestamp){ 
        // 1. 根据信道ID找到对应的信道
        Channel::ptr channel = _channel_manager->get(message->cid());
        if (channel.get() == nullptr) { 
            DLOG("未找到信道信息!"); 
            return; 
        } 
        
        // 2. 封装异步任务(消息处理任务),放入线程池执行
        _worker->pool.push([channel, message](){ 
            channel->consume(message); 
        }); 
    } 

    // 处理未知消息类型
    // conn: TCP连接指针
    // message: 未知消息
    // timestamp: 时间戳
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn,  
                         const MessagePtr& message, 
                         muduo::Timestamp) { 
        LOG_INFO << "onUnknownMessage: " << message->GetTypeName(); 
        conn->shutdown(); // 关闭连接
    } 

    // TCP连接状态变化回调
    // conn: TCP连接指针
    void onConnection(const muduo::net::TcpConnectionPtr& conn){ 
        if (conn->connected()) { 
            // 连接建立成功
            _conn = conn; // 保存连接指针
            _latch.countDown();//唤醒等待的主线程
        } else { 
            // 连接关闭时的清理操作
            _conn.reset(); // 重置连接指针
        } 
    } 

private: 
    muduo::CountDownLatch _latch;          // 同步原语,用于等待连接建立
    muduo::net::TcpConnectionPtr _conn;    // 客户端对应的TCP连接指针
    muduo::net::TcpClient _client;         // Muduo TCP客户端对象
    ProtobufDispatcher _dispatcher;        // 协议消息分发器
    ProtobufCodecPtr _codec;               // 协议编解码器指针
    AsyncWorker::ptr _worker;              // 异步工作器指针
    ChannelManager::ptr _channel_manager;  // 信道管理器指针
}; 

} 

#endif
相关推荐
MC丶科19 小时前
【SpringBoot常见报错与解决方案】中文乱码?Spring Boot 统一解决前后端中文乱码问题(含 Postman 测试)!别再百度“加 UTF-8”了!
spring boot·后端·postman
leaves falling20 小时前
C语言内存函数-
c语言·开发语言
至为芯1 天前
IP6537至为芯支持双C口快充输出的45W降压SOC芯片
c语言·开发语言
小羊羊Python1 天前
SoundMaze v1.0.1正式发布!
开发语言·c++
浩瀚地学1 天前
【Java】JDK8的一些新特性
java·开发语言·经验分享·笔记·学习
l1t1 天前
利用DeepSeek将python DLX求解数独程序格式化并改成3.x版本
开发语言·python·算法·数独
XXOOXRT1 天前
基于SpringBoot的加法计算器
java·spring boot·后端·html5
yugi9878381 天前
基于遗传算法优化主动悬架模糊控制的Matlab实现
开发语言·matlab
moxiaoran57531 天前
Go语言的错误处理
开发语言·后端·golang
yugi9878381 天前
MATLAB的多层感知器(MLP)与极限学习机(ELM)实现
开发语言·matlab