【消息队列项目】服务器实现

一.通信协议设计与实现

在这个模块,我们需要制定好我们的网络通信协议,那么由于我们使用的是Muduo库来进行数据的传输,那么Muduo库里面进行数据传输的时候,Muduo库它自己内部已经定制好了一个传输协议

  • 一个完整的数据包应该包含:4字节+真实数据

注意前面4个字节是用于存储后面真实数据的大小的

具体的处理过程就在这个ProtobufCodec类的onMessage函数

cpp 复制代码
void ProtobufCodec::onMessage(const TcpConnectionPtr& conn,
                              Buffer* buf,
                              Timestamp receiveTime)
{
  while (buf->readableBytes() >= kMinMessageLen + kHeaderLen)
  {
    const int32_t len = buf->peekInt32();
    //陈硕大佬在处理protobuf的时候,自定义了一个协议来处理粘包问题,
    //一个完整的数据包里面的前32位比特位存储着整个数据包的大小
    //先读取了一个整型的数据(4个字节),为了获取一个包的前4个字节,这里存储着整个数据包的大小
    //此时len就代表数据有多长了
    if (len > kMaxMessageLen || len < kMinMessageLen)
    {
      errorCallback_(conn, buf, receiveTime, kInvalidLength);
      break;
    }
    else if (buf->readableBytes() >= implicit_cast<size_t>(len + kHeaderLen))
    {
      ErrorCode errorCode = kNoError;
      //typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
      //我们自己在protobuf自定义的那个message的父类就是google::protobuf::Message
      //先在缓冲区里面读取指定长度的信息,然后再进行反序列化parse进行解析
      MessagePtr message = parse(buf->peek()+kHeaderLen, len, &errorCode);
      //可以理解为这个message里面就获取到了我们通过protobuf反序列化后的数据
      if (errorCode == kNoError && message)//没有出错且消息存在
      {
        //messageCallback_;就是我们在构建ProtobufCodec时传入的那个回调函数
        messageCallback_(conn, message, receiveTime);//进行消息处理
        buf->retrieve(kHeaderLen+len);
      }
      else
      {
        errorCallback_(conn, buf, receiveTime, errorCode);
        break;
      }
    }
    else
    {
      break;
    }
  }
}

事实上,仅仅只是上面那个协议还是不行的,Muduo库就基于上面那个格式来接着定义了下面这么一个通信协议

请求/响应报文结构

各字段说明:

  • len:4个字节, 表示整个报文的长度(注意不包含len这4个字节)
  • nameLen: 4个字节, 表示typeName数组的长度
  • typeName:是个字节数组, 占nameLen个字节, 表示请求/响应报文的类型名, 作用是分发不同消息到对应的远端接口调用中
  • protobufData:是个字节数组, 占len - nameLen - 8个字节, 表示请求/响应参数数据通过protobuf序列化之后的二进制数据
  • checkSum:4个字节, 表示整个消息的校验和, 作用是为了校验请求/响应报文的完整性

具体的实现过程在code.cc里面,我们可以看看

cpp 复制代码
#include "examples/protobuf/codec/codec.h"

#include "muduo/base/Logging.h"
#include "muduo/net/Endian.h"
#include "muduo/net/protorpc/google-inl.h"

#include <google/protobuf/descriptor.h>

#include <zlib.h>  // adler32 - 用于校验和计算

using namespace muduo;
using namespace muduo::net;

// 编码函数:将Protobuf消息序列化为自定义协议格式
// 协议格式:[4字节总长度][4字节类型名长度][类型名字符串(含\0)][Protobuf数据][4字节校验和]
void ProtobufCodec::fillEmptyBuffer(Buffer* buf, const google::protobuf::Message& message)
{
  // 清空缓冲区,确保从空缓冲区开始
  // buf->retrieveAll();
  assert(buf->readableBytes() == 0);

  // 第1步:获取并序列化消息类型名
  // 格式:[4字节类型名长度][类型名字符串(含\0)]
  const std::string& typeName = message.GetTypeName();
  int32_t nameLen = static_cast<int32_t>(typeName.size()+1);  // +1包含\0结束符
  buf->appendInt32(nameLen);  // 写入类型名长度(4字节)
  buf->append(typeName.c_str(), nameLen);  // 写入类型名字符串(含\0)

  // 第2步:序列化Protobuf消息体
  // 验证消息已正确初始化
  GOOGLE_DCHECK(message.IsInitialized()) << InitializationErrorMessage("serialize", message);

  /**
   * Protobuf版本兼容性处理
   * ByteSize()在Protobuf v3.4.0后已弃用,使用ByteSizeLong()替代
   */
  #if GOOGLE_PROTOBUF_VERSION > 3009002
    int byte_size = google::protobuf::internal::ToIntSize(message.ByteSizeLong());
  #else
    int byte_size = message.ByteSize();
  #endif
  
  // 确保缓冲区有足够空间
  buf->ensureWritableBytes(byte_size);

  // 序列化Protobuf消息到缓冲区
  uint8_t* start = reinterpret_cast<uint8_t*>(buf->beginWrite());
  uint8_t* end = message.SerializeWithCachedSizesToArray(start);
  
  // 验证序列化后的字节数与预期一致
  if (end - start != byte_size)
  {
    #if GOOGLE_PROTOBUF_VERSION > 3009002
      ByteSizeConsistencyError(byte_size, google::protobuf::internal::ToIntSize(message.ByteSizeLong()), static_cast<int>(end - start));
    #else
      ByteSizeConsistencyError(byte_size, message.ByteSize(), static_cast<int>(end - start));
    #endif
  }
  buf->hasWritten(byte_size);  // 更新写入位置

  // 第3步:计算并添加校验和
  // 使用Adler-32算法计算校验和,校验范围包括类型名长度到Protobuf数据的所有字节
  int32_t checkSum = static_cast<int32_t>(
      ::adler32(1,
                reinterpret_cast<const Bytef*>(buf->peek()),
                static_cast<int>(buf->readableBytes())));
  buf->appendInt32(checkSum);  // 写入校验和(4字节)
  
  // 验证当前缓冲区数据大小符合预期
  // 总大小 = 类型名长度(4) + 类型名字符串(nameLen) + Protobuf数据(byte_size) + 校验和(4)
  assert(buf->readableBytes() == sizeof nameLen + nameLen + byte_size + sizeof checkSum);
  
  // 第4步:在数据最前面添加总长度字段
  // 总长度字段不包括自身这4个字节,因此是当前缓冲区的全部数据长度
  int32_t len = sockets::hostToNetwork32(static_cast<int32_t>(buf->readableBytes()));
  buf->prepend(&len, sizeof len);  // 在缓冲区最前面添加总长度
}

//
// no more google code after this
//

//
// FIXME: merge with RpcCodec
//

namespace
{
  // 错误码对应的字符串描述
  const string kNoErrorStr = "NoError";
  const string kInvalidLengthStr = "InvalidLength";
  const string kCheckSumErrorStr = "CheckSumError";
  const string kInvalidNameLenStr = "InvalidNameLen";
  const string kUnknownMessageTypeStr = "UnknownMessageType";
  const string kParseErrorStr = "ParseError";
  const string kUnknownErrorStr = "UnknownError";
}

// 错误码转换为字符串
const string& ProtobufCodec::errorCodeToString(ErrorCode errorCode)
{
  switch (errorCode)
  {
   case kNoError:
     return kNoErrorStr;
   case kInvalidLength:
     return kInvalidLengthStr;
   case kCheckSumError:
     return kCheckSumErrorStr;
   case kInvalidNameLen:
     return kInvalidNameLenStr;
   case kUnknownMessageType:
     return kUnknownMessageTypeStr;
   case kParseError:
     return kParseErrorStr;
   default:
     return kUnknownErrorStr;
  }
}

// 默认错误回调函数
void ProtobufCodec::defaultErrorCallback(const muduo::net::TcpConnectionPtr& conn,
                                         muduo::net::Buffer* buf,
                                         muduo::Timestamp,
                                         ErrorCode errorCode)
{
  LOG_ERROR << "ProtobufCodec::defaultErrorCallback - " << errorCodeToString(errorCode);
  if (conn && conn->connected())
  {
    conn->shutdown();  // 发生错误时关闭连接
  }
}

// 辅助函数:从网络字节序中读取32位整数
int32_t asInt32(const char* buf)
{
  int32_t be32 = 0;
  ::memcpy(&be32, buf, sizeof(be32));
  return sockets::networkToHost32(be32);  // 网络字节序转为主机字节序
}

// TCP消息到达时的回调函数
// 这个函数负责从TCP流中解析出自定义协议格式的数据包
void ProtobufCodec::onMessage(const TcpConnectionPtr& conn,
                              Buffer* buf,
                              Timestamp receiveTime)
{
  // 循环处理缓冲区中的数据,直到没有完整的数据包
  while (buf->readableBytes() >= kMinMessageLen + kHeaderLen)
  {
    // 步骤1:读取数据包总长度字段(4字节)
    const int32_t len = buf->peekInt32();  // 获取数据包总长度(不包括这4个字节本身)
    
    // 步骤2:验证长度是否合法
    if (len > kMaxMessageLen || len < kMinMessageLen)
    {
      errorCallback_(conn, buf, receiveTime, kInvalidLength);
      break;  // 长度非法,停止处理
    }
    // 步骤3:检查是否有完整的数据包
    else if (buf->readableBytes() >= implicit_cast<size_t>(len + kHeaderLen))
    {
      ErrorCode errorCode = kNoError;
      // 步骤4:解析数据包(跳过前4字节的长度字段)
      MessagePtr message = parse(buf->peek()+kHeaderLen, len, &errorCode);
      
      if (errorCode == kNoError && message)  // 解析成功
      {
        messageCallback_(conn, message, receiveTime);  // 调用消息回调处理消息
        buf->retrieve(kHeaderLen+len);  // 从缓冲区中移除已处理的数据
      }
      else  // 解析失败
      {
        errorCallback_(conn, buf, receiveTime, errorCode);
        break;  // 发生错误,停止处理
      }
    }
    else  // 数据不完整,等待更多数据
    {
      break;
    }
  }
}

// 根据类型名动态创建Protobuf消息对象
// 使用Protobuf的反射机制根据类型名创建对应的消息实例
google::protobuf::Message* ProtobufCodec::createMessage(const std::string& typeName)
{
  google::protobuf::Message* message = NULL;
  // 步骤1:根据类型名获取消息描述符
  const google::protobuf::Descriptor* descriptor =
    google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
  
  if (descriptor)
  {
    // 步骤2:根据描述符获取原型对象
    const google::protobuf::Message* prototype =
      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
    
    if (prototype)
    {
      // 步骤3:克隆原型创建新实例
      message = prototype->New();
    }
  }
  return message;
}

// 解析函数:将字节流解析为Protobuf消息对象
// buf: 指向数据的指针(不包括前4字节的长度字段)
// len: 数据长度(不包括前4字节的长度字段)
MessagePtr ProtobufCodec::parse(const char* buf, int len, ErrorCode* error)
{
  MessagePtr message;

  // 步骤1:校验和验证
  // 获取期望的校验和(数据包的最后4个字节)
  int32_t expectedCheckSum = asInt32(buf + len - kHeaderLen);
  // 计算实际校验和(校验范围:从类型名长度到Protobuf数据结束,不包括校验和自身)
  int32_t checkSum = static_cast<int32_t>(
      ::adler32(1,
                reinterpret_cast<const Bytef*>(buf),
                static_cast<int>(len - kHeaderLen)));
  
  if (checkSum == expectedCheckSum)  // 校验和通过
  {
    // 步骤2:获取消息类型名
    int32_t nameLen = asInt32(buf);  // 读取类型名长度
    if (nameLen >= 2 && nameLen <= len - 2*kHeaderLen)  // 验证类型名长度合法
    {
      // 提取类型名字符串(去掉末尾的\0)
      std::string typeName(buf + kHeaderLen, buf + kHeaderLen + nameLen - 1);
      
      // 步骤3:根据类型名动态创建消息对象
      message.reset(createMessage(typeName));
      
      if (message)  // 成功创建消息对象
      {
        // 步骤4:解析Protobuf数据
        const char* data = buf + kHeaderLen + nameLen;  // Protobuf数据起始位置
        int32_t dataLen = len - nameLen - 2*kHeaderLen;  // Protobuf数据长度
        
        if (message->ParseFromArray(data, dataLen))  // 反序列化Protobuf数据
        {
          *error = kNoError;  // 解析成功
        }
        else
        {
          *error = kParseError;  // Protobuf解析失败
        }
      }
      else  // 未知的消息类型
      {
        *error = kUnknownMessageType;
      }
    }
    else  // 类型名长度非法
    {
      *error = kInvalidNameLen;
    }
  }
  else  // 校验和错误
  {
    *error = kCheckSumError;
  }

  return message;
}

这个大家自己看看就行了,我们接下来来设计我们的服务器

二.服务器代码编写

我们服务器的代码都是存放在下面的broker.hpp里面的

我们这个类里面的成员主要包含下面这些

  • _server:Muduo库提供的一个通用TCP服务器, 我们可以封装这个服务器进行TCP通信
  • _baseloop:主事件循环器, 用于响应IO事件和定时器事件,主loop主要是为了响应监听描述符的IO事件
  • _codec: 一个protobuf编解码器, 我们在TCP服务器上设计了一层应用层协议, 这个编解码器主要就是负责实现应用层协议的解析和封装, 下边具体讲解
  • _dispatcher:一个消息分发器, 当Socket接收到一个报文消息后, 我们需要按照消息的类型, 即上面提到的typeName进行消息分发, 会不不同类型的消息分发相对应的的处理函数中,下边具体讲解
  • _consumer: 服务器中的消费者信息管理句柄。
  • _threadpool: 异步工作线程池,主要用于队列消息的推送工作。
  • _connections: 连接管理句柄,管理当前服务器上的所有已经建立的通信连接。
  • _virtual_host:服务器持有的虚拟主机。 队列、交换机 、绑定、消息等数据都是通过虚拟主机管理
cpp 复制代码
// 防止头文件重复包含的预处理指令
#ifndef __M_BROKER_H__
#define __M_BROKER_H__

// 包含必要的头文件
#include "../third/include/muduo/protobuf/codec.h"
#include "../third/include/muduo/protobuf/dispatcher.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/TcpServer.h"
#include "../mqcommon/threadpool.hpp"
#include "../mqcommon/msg.pb.h"
#include "../mqcommon/proto.pb.h"
#include "../mqcommon/logger.hpp"
#include "connection.hpp"
#include "consumer.hpp"
#include "host.hpp"

// 定义命名空间为mymq
namespace mymq
{

// 定义数据库文件路径常量
#define DBFILE "/meta.db"
// 定义虚拟主机名称常量
#define HOSTNAME "MyVirtualHost"

    // Server类定义,作为消息队列Broker的核心服务器类
    class Server
    {
    public:
        // 定义消息指针类型别名,使用智能指针管理protobuf消息对象
        typedef std::shared_ptr<google::protobuf::Message> MessagePtr;

        // 构造函数:初始化服务器组件和注册消息处理器
        // @param port: 服务器监听端口号
        // @param basedir: 基础目录路径,用于存储元数据和消息数据
        Server(int port, const std::string &basedir) : // 初始化服务器事件循环对象
                                                       _baseloop(),
                                                       // 初始化TCP服务器对象,绑定指定端口,支持端口复用
                                                       _server(&_baseloop,
                                                               muduo::net::InetAddress("0.0.0.0", port),
                                                               "Server", muduo::net::TcpServer::kReusePort),
                                                       // 初始化消息分发器,设置未知消息处理函数
                                                       _dispatcher(std::bind(&Server::onUnknownMessage,//遇到消息类型未知的消息,就会调用Server::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))),
                                                       // 初始化虚拟主机,管理交换机、队列等资源
                                                       _virtual_host(std::make_shared<VirtualHost>(HOSTNAME,
                                                                                                   basedir, basedir + DBFILE)),
                                                       // 初始化消费者管理器,管理所有队列的消费者
                                                       _consumer_manager(std::make_shared<ConsumerManager>()),
                                                       // 初始化连接管理器,管理所有客户端连接
                                                       _connection_manager(std::make_shared<ConnectionManager>()),
                                                       // 初始化线程池,用于处理业务逻辑
                                                       _threadpool(std::make_shared<ThreadPool>())
        {

            // 初始化:针对所有历史队列初始化消费者管理结构
            // 从虚拟主机获取所有队列信息
            QueueMap qm = _virtual_host->allQueues();
            for (auto &q : qm)
            {
                // 为每个队列初始化消费者管理器中的对应结构
                _consumer_manager->initQueueConsumer(q.first);//根据队列名来
            }

            // 注册各类消息处理回调函数到分发器

            // 打开信道请求处理
            //遇到消息类型是mymq::openChannelRequest的消息,就调用Server::onOpenChannel函数去处理
            _dispatcher.registerMessageCallback<mymq::openChannelRequest>(
                std::bind(&Server::onOpenChannel, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 关闭信道请求处理
            //遇到消息类型是mymq::closeChannelRequest的消息,就调用Server::onCloseChannel函数去处理
            _dispatcher.registerMessageCallback<mymq::closeChannelRequest>(
                std::bind(&Server::onCloseChannel, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 声明交换机请求处理
            //遇到消息类型是mymq::declareExchangeRequest的消息,就调用Server::onDeclareExchange函数去处理
            _dispatcher.registerMessageCallback<mymq::declareExchangeRequest>(
                std::bind(&Server::onDeclareExchange, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 删除交换机请求处理
            //遇到消息类型是mymq::deleteExchangeRequest的消息,就调用Server::onDeleteExchange函数去处理
            _dispatcher.registerMessageCallback<mymq::deleteExchangeRequest>(
                std::bind(&Server::onDeleteExchange, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 声明队列请求处理
            //遇到消息类型是mymq::declareQueueRequest的消息,就调用Server::onDeclareQueue函数去处理
            _dispatcher.registerMessageCallback<mymq::declareQueueRequest>(
                std::bind(&Server::onDeclareQueue, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 删除队列请求处理
            //遇到消息类型是mymq::deleteQueueRequest的消息,就调用Server::onDeleteQueue函数去处理
            _dispatcher.registerMessageCallback<mymq::deleteQueueRequest>(
                std::bind(&Server::onDeleteQueue, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 队列绑定请求处理
            //遇到消息类型是mymq::queueBindRequest的消息,就调用Server::onQueueBind函数去处理
            _dispatcher.registerMessageCallback<mymq::queueBindRequest>(
                std::bind(&Server::onQueueBind, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 队列解绑请求处理
            //遇到消息类型是mymq::queueUnBindRequest的消息,就调用Server::onQueueUnBind函数去处理
            _dispatcher.registerMessageCallback<mymq::queueUnBindRequest>(
                std::bind(&Server::onQueueUnBind, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 消息发布请求处理
            //遇到消息类型是mymq::basicPublishRequest的消息,就调用Server::onBasicPublish函数去处理
            _dispatcher.registerMessageCallback<mymq::basicPublishRequest>(
                std::bind(&Server::onBasicPublish, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 消息确认请求处理
            //遇到消息类型是mymq::basicAckRequest的消息,就调用Server::onBasicAck函数去处理
            _dispatcher.registerMessageCallback<mymq::basicAckRequest>(
                std::bind(&Server::onBasicAck, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 消息消费请求处理
            //遇到消息类型是mymq::basicConsumeRequest的消息,就调用Server::onBasicConsume函数去处理
            _dispatcher.registerMessageCallback<mymq::basicConsumeRequest>(
                std::bind(&Server::onBasicConsume, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 取消消费请求处理
            //遇到消息类型是mymq::basicCancelRequest的消息,就调用Server::onBasicCancel函数去处理
            _dispatcher.registerMessageCallback<mymq::basicCancelRequest>(
                std::bind(&Server::onBasicCancel, this,
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 设置服务器的消息回调函数:将TCP消息传递给协议编解码器处理
            _server.setMessageCallback(
                std::bind(&ProtobufCodec::onMessage,
                          _codec.get(),
                          std::placeholders::_1, 
                          std::placeholders::_2,
                          std::placeholders::_3));

            // 设置服务器的连接状态回调函数
            //连接成功和断开的时候都会去自动调用Server::onConnection
            _server.setConnectionCallback(std::bind(&Server::onConnection,
                                                    this, std::placeholders::_1));
        }

        // 启动服务器:开始监听并进入事件循环
        void start()
        {
            // 启动TCP服务器,开始监听客户端连接
            _server.start();
            // 启动事件循环,处理网络事件和定时任务
            _baseloop.loop();
        }

    private:
        // 打开信道请求处理函数
        // @param conn: TCP连接对象指针
        // @param message: 打开信道请求消息指针
        // @param: 时间戳,标识消息接收时间
        void onOpenChannel(const muduo::net::TcpConnectionPtr &conn,
                           const openChannelRequestPtr &message,
                           muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                // 日志记录:未找到对应的Connection对象
                DLOG("打开信道时,没有找到连接对应的Connection对象!");
                // 关闭异常连接
                conn->shutdown();
                return;
            }
            // 调用Connection对象的打开信道方法
            return mconn->openChannel(message);
        }

        // 关闭信道请求处理函数
        void onCloseChannel(const muduo::net::TcpConnectionPtr &conn,
                            const closeChannelRequestPtr &message,
                            muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("关闭信道时,没有找到连接对应的Connection对象!");
                conn->shutdown();// 关闭异常连接
                return;
            }
            return mconn->closeChannel(message);// 调用Connection对象的关闭信道方法
        }

        // 声明交换机请求处理函数
        void onDeclareExchange(const muduo::net::TcpConnectionPtr &conn,
                               const declareExchangeRequestPtr &message,
                               muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("声明交换机时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            // 从Connection对象获取指定信道ID的信道对象
            Channel::ptr cp = mconn->getChannel(message->cid());//根据信道ID来找到对应的信道对象
            if (cp.get() == nullptr)
            {
                DLOG("声明交换机时,没有找到信道!");
                return;
            }
            // 调用信道对象的声明交换机方法
            return cp->declareExchange(message);//通过信道来进行数据交互
        }

        // 删除交换机请求处理函数
        void onDeleteExchange(const muduo::net::TcpConnectionPtr &conn,
                              const deleteExchangeRequestPtr &message,
                              muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("删除交换机时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            // 从Connection对象获取指定信道ID的信道对象
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("删除交换机时,没有找到信道!");
                return;
            }
            // 调用信道对象的删除交换机方法
            return cp->deleteExchange(message);
        }

        // 声明队列请求处理函数
        void onDeclareQueue(const muduo::net::TcpConnectionPtr &conn,
                            const declareQueueRequestPtr &message,
                            muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("声明队列时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            // 从Connection对象获取指定信道ID的信道对象
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("声明队列时,没有找到信道!");
                return;
            }
            // 调用信道对象的声明队列方法
            return cp->declareQueue(message);
        }

        // 删除队列请求处理函数
        void onDeleteQueue(const muduo::net::TcpConnectionPtr &conn,
                           const deleteQueueRequestPtr &message,
                           muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("删除队列时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            // 从Connection对象获取指定信道ID的信道对象
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("删除队列时,没有找到信道!");
                return;
            }
            // 调用信道对象的删除队列方法
            return cp->deleteQueue(message);
        }

        // 队列绑定请求处理函数
        void onQueueBind(const muduo::net::TcpConnectionPtr &conn,
                         const queueBindRequestPtr &message,
                         muduo::Timestamp)
        {
            // 根据TCP连接获取对应的Connection对象
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("队列绑定时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            // 从Connection对象获取指定信道ID的信道对象
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("队列绑定时,没有找到信道!");
                return;
            }
            // 调用信道对象的绑定队列到交换机方法
            return cp->queueBind(message);
        }

        // 队列解绑请求处理函数
        void onQueueUnBind(const muduo::net::TcpConnectionPtr &conn,
                           const queueUnBindRequestPtr &message,
                           muduo::Timestamp)
        {
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("队列解除绑定时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("队列解除绑定时,没有找到信道!");
                return;
            }
            return cp->queueUnBind(message);
        }

        // 消息发布请求处理函数
        void onBasicPublish(const muduo::net::TcpConnectionPtr &conn,
                            const basicPublishRequestPtr &message,
                            muduo::Timestamp)
        {
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("发布消息时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("发布消息时,没有找到信道!");
                return;
            }
            return cp->basicPublish(message);
        }

        // 消息确认请求处理函数
        void onBasicAck(const muduo::net::TcpConnectionPtr &conn,
                        const basicAckRequestPtr &message,
                        muduo::Timestamp)
        {
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("确认消息时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("确认消息时,没有找到信道!");
                return;
            }
            return cp->basicAck(message);
        }

        // 队列消息订阅请求处理函数
        void onBasicConsume(const muduo::net::TcpConnectionPtr &conn,
                            const basicConsumeRequestPtr &message,
                            muduo::Timestamp)
        {
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("队列消息订阅时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("队列消息订阅时,没有找到信道!");
                return;
            }
            return cp->basicConsume(message);
        }

        // 队列消息取消订阅请求处理函数
        void onBasicCancel(const muduo::net::TcpConnectionPtr &conn,
                           const basicCancelRequestPtr &message,
                           muduo::Timestamp)
        {
            Connection::ptr mconn = _connection_manager->getConnection(conn);
            if (mconn.get() == nullptr)
            {
                DLOG("队列消息取消订阅时,没有找到连接对应的Connection对象!");
                conn->shutdown();
                return;
            }
            Channel::ptr cp = mconn->getChannel(message->cid());
            if (cp.get() == nullptr)
            {
                DLOG("队列消息取消订阅时,没有找到信道!");
                return;
            }
            return cp->basicCancel(message);
        }

        // 未知消息处理函数:当接收到未注册处理器的消息类型时调用
        void onUnknownMessage(const muduo::net::TcpConnectionPtr &conn,
                              const MessagePtr &message, muduo::Timestamp)
        {
            // 记录未知消息的日志信息
            LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
            // 关闭异常连接
            conn->shutdown();
        }

        // 连接状态变化回调函数
        // @param conn: TCP连接对象指针
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                // 新连接建立时,创建对应的Connection对象并注册到连接管理器
                _connection_manager->newConnection(_virtual_host,
                                                   _consumer_manager, _codec, conn,
                                                   _threadpool);
            }
            else
            {
                // 连接断开时,从连接管理器中删除对应的Connection对象
                _connection_manager->delConnection(conn);
            }
        }

    private:
        // 事件循环对象,用于处理网络事件和定时任务
        muduo::net::EventLoop _baseloop;
        // TCP服务器对象,处理客户端连接和网络通信
        muduo::net::TcpServer _server;
        // 消息分发器对象,根据消息类型路由到对应的处理器函数
        ProtobufDispatcher _dispatcher;
        // protobuf协议处理器指针,用于编解码protobuf消息
        ProtobufCodecPtr _codec;
        // 虚拟主机指针,管理交换机、队列、绑定关系等资源
        VirtualHost::ptr _virtual_host;
        // 消费者管理器指针,管理所有队列的消费者信息
        ConsumerManager::ptr _consumer_manager;
        // 连接管理器指针,管理所有客户端连接和对应的Connection对象
        ConnectionManager::ptr _connection_manager;
        // 线程池指针,用于处理业务逻辑任务
        ThreadPool::ptr _threadpool;
    };

} // namespace mymq 结束

#endif // __M_BROKER_H__ 头文件结束

就很简单

三.编译测试

test.cpp

cpp 复制代码
#include"../../mqserver/broker.hpp"

int main()
{
    mymq::Server server(8085,"./data/");
    server.start();
}

makefile

cpp 复制代码
test:test.cpp ../../third/include/muduo/protobuf/codec.cc ../../mqcommon/msg.pb.cc ../../mqcommon/proto.pb.cc
	g++ $^ -o $@  -I ../../third/include -L ../../third/lib -lmuduo_net -lmuduo_base -lpthread -lprotobuf -lz -lsqlite3

这个时候我们换一个终端

现在是一点问题都没有了。

相关推荐
一只旭宝2 小时前
Linux专题十:I/O 复用进阶(LT/ET 模式)同步,异步阻塞,以及 libevent 库核心知识点
linux·服务器·网络
Gofarlic_oms12 小时前
区块链存证节点搭建:金融行业审计证据链构建指南
运维·人工智能·金融·数据挖掘·区块链·需求分析·devops
回忆是昨天里的海2 小时前
docker Compose-安装wordpress
运维·docker·容器
iconball2 小时前
个人用云计算学习笔记 --31 华为云运维服务
运维·笔记·学习·华为云·云计算
想做后端的小C2 小时前
Linux:期末考点
linux·运维·服务器
我可以将你更新哟2 小时前
【linux】配置 Docker 国内镜像源, centos7安装docker-ce,docker相关命令,永久配置 DNS
linux·运维·docker
jimy12 小时前
本地下载vscode server安装包(tar.gz)然后上传至服务器开发机
服务器·ide·vscode
等什么君!2 小时前
nginx启动失败 ,报404和 idea端口号被占用的解决办法
运维·nginx
machunlin~2 小时前
centos 系统安装相关
linux·运维·docker·centos