仿RabbitMQ实现消息队列-项目设计

项目介绍

曾经我们学习过 阻塞队列 (BlockingQueue)

当时我们说阻塞队列最大的用途,就是用来实现 生产者消费者模型

生产者消费者模型是后端开发的常用编程方式,它存在诸多优势:

  • 解耦合
  • 支持并发执行
  • 适配系统忙闲不均场景
  • 削峰填谷,流量缓冲
  • 等等

在实际后端开发中,尤其是分布式系统里,跨主机通信使用生产者消费者模型是非常普遍的需求。

因此,我们会将阻塞队列封装为独立服务程序,并拓展丰富能力,这类专门用于异步通信、数据缓冲的中间件,就统称为 消息队列(Message Queue,MQ)

市面主流成熟消息队列组件:

  • RabbitMQ
  • Kafka
  • RocketMQ
  • ActiveMQ

其中 RabbitMQ 功能完善、生态成熟、使用广泛。下文将仿照 RabbitMQ 核心思想,手写模拟实现一个简易版消息队列

项目结构设计

核心概念:

  • 生产者 (Producer)
  • 消费者 (Consumer)
  • 中间人 (Broker)
  • 发布 (Publish)
  • 订阅 (Subscribe)

其中, Broker Server是最核心的部分, 负责消息的存储和转发。

AMQP

我们的MQ设计遵循AMQP(Advanced Message Queuing Protocol-高级消息队列协议),一个提供统一消息服务的应用层标准高级消息队列协议,为面向消息的中间件设计,使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能)模型中,也就是消息中间件服务器Broker中,又存在以下概念:

  • 虚拟机 (VirtualHost): 类似于 MySQL 的 "database", 是一个逻辑上的集合。一个 BrokerServer 上可以存在多个 VirtualHost
  • 交换机 (Exchange): 生产者把消息先发送到 Broker 的 Exchange 上,再根据不同的规则, 把消息转发给不同的 Queue
  • 队列 (Queue): 真正用来存储消息的部分, 每个消费者决定自己从哪个 Queue 上读取消息
  • 绑定 (Binding): Exchange 和 Queue 之间的关联关系,Exchange 和 Queue 可以理解成 "多对多" 关系,使用一个关联表就可以把这两个概念联系起来
  • 消息 (Message): 传递的内容

上述数据结构, 既需要在内存中存储, 也需要在硬盘中存储

  • 内存存储: 方便使用
  • 硬盘存储: 重启数据不丢失

核心 API

对于 Broker 来说,需要实现以下核心 API,完成消息队列的基础功能:

  1. 创建交换机(exchangeDeclare
  2. 销毁交换机(exchangeDelete
  3. 创建队列(queueDeclare
  4. 销毁队列(queueDelete
  5. 创建绑定(queueBind
  6. 解除绑定(queueUnbind
  7. 发布消息(basicPublish
  8. 订阅消息(basicConsume
  9. 确认消息(basicAck
  10. 取消订阅(basicCancel

另一方面,Producer(生产者)Consumer(消费者) 通过网络远程调用这些 API,最终实现完整的生产者-消费者模型。

交换机类型

RabbitMQ 主要支持以下四种交换机类型:

  • Direct:直连模式
  • Fanout:广播模式
  • Topic:主题模式
  • Header:头信息模式

其中 Header 模式使用复杂、实际场景极少,项目中仅需实现前三种核心类型

三种常用交换机说明

  • Direct:生产者发送消息时,直接指定绑定该交换机的队列名称,消息精准投递到对应队列。
  • Fanout :广播模式,生产者发送的消息会被复制并转发到与该交换机绑定的所有队列
  • Topic :主题模式,绑定时指定 bindingKey,发送消息时指定 routingKey;满足匹配规则时,消息自动投递到目标队列。

持久化

Exchange, Queue, Binding, Message 等数据都有持久化需求

当程序重启 / 主机重启, 保证上述内容不丢失

网络通信

生产者和消费者都属于客户端程序Broker 作为服务端,双方通过网络完成通信。

在网络通信过程中,客户端需要提供以下 API 实现对服务端的操作:

  1. 创建 Connection
  2. 关闭 Connection
  3. 创建 Channel
  4. 关闭 Channel
  5. 创建队列(queueDeclare)
  6. 销毁队列(queueDelete)
  7. 创建交换机(exchangeDeclare)
  8. 销毁交换机(exchangeDelete)
  9. 创建绑定(queueBind)
  10. 解除绑定(queueUnbind)
  11. 发布消息(basicPublish)
  12. 订阅消息(basicConsume)
  13. 确认消息(basicAck)
  14. 取消订阅(basicCancel)

核心概念说明

可以看到,在 Broker 提供的 API 基础上,客户端额外增加了 Connection 和 Channel 相关操作

  • Connection :对应一个 TCP 连接
  • Channel :Connection 内部的逻辑通道

一个 TCP 连接(Connection)中可以包含多个逻辑通道(Channel),不同 Channel 之间数据相互隔离、互不干扰。

这种设计可以高效复用 TCP 连接,实现长连接机制,避免频繁创建和销毁 TCP 连接带来的性能开销。

消息应答

被消费的消息, 需要进行应答。应答模式分成两种:

  • 自动应答: 消费者只要消费了消息, 就算应答完毕了,Broker 直接删除这个消息
  • 手动应答: 消费者手动调用应答接口, Broker 收到应答请求之后, 才真正删除这个消息

消息队列系统模块划分

整体分为三大部分实现:

  1. 服务端
  2. 发布客户端
  3. 订阅客户端

一、服务端模块

1. 数据管理模块

负责核心数据的增删查管理与持久化存储,分为四个子模块:

  • 交换机数据管理模块:管理交换机的创建、删除与元数据维护
  • 队列数据管理模块:管理队列的创建、删除与消息存储
  • 绑定数据管理模块:维护交换机与队列之间的绑定关系
  • 消息数据管理模块:负责消息的存储、分发与状态管理

2. 虚拟机数据管理模块

虚拟机是交换机、队列、绑定关系与消息的整体逻辑单元。该模块负责对上述四个数据管理模块进行统一整合与管理,实现逻辑隔离。

3. 交换路由模块

负责消息的发布与路由匹配逻辑:

  • 接收生产者发布的消息,根据交换机类型决定消息投递到哪些队列
  • 支持 Direct(直连)、Fanout(广播)、Topic(主题)三种核心模式
  • 其中 Topic 模式需实现 routingKeybindingKey 的规则匹配

4. 消费者管理模块

管理所有订阅队列的客户端:

  • 维护客户端与队列的订阅关系
  • 当队列有消息时,通过信道推送给对应消费者
  • 支持取消订阅、重连等场景下的消费者状态维护

5. 信道(通信通道)管理模块

管理连接内的逻辑通信通道:

  • 一个 TCP 连接可包含多个独立的信道,实现连接复用
  • 客户端关闭通信时,仅需关闭对应信道,无需断开整个连接

6. 连接管理模块

维护客户端与服务端的 TCP 连接:

  • 管理连接的创建、心跳检测与关闭
  • 连接关闭时,自动清理该连接下的所有信道资源

7. BrokerServer 整合模块

对以上所有服务端模块进行整合,封装为完整的消息队列服务端程序,对外提供统一的 API 与通信入口。


二、客户端模块

1. 消费者管理模块

客户端侧的消费者逻辑封装:当订阅某个队列时,自动创建并维护消费者实例,处理服务端推送的消息。

2. 信道管理模块

与服务端信道一一对应,封装信道相关的请求(声明队列、发布消息、订阅消费等),为用户提供便捷的操作接口。

3. 连接管理模块

管理客户端与服务端的 TCP 连接,整合信道资源,用户视角下所有请求均通过信道完成,无需直接操作底层连接。

4. 客户端封装实现

基于以上模块,封装两种核心客户端:

  • 订阅客户端:订阅指定队列,接收服务端推送的消息并处理
  • 发布客户端:向指定交换机发布消息,无需关心后续路由与消费逻辑

关系图

管理
包含
管理
包含
包含
包含
绑定
绑定
存储
订阅
关联
1
1
1
1
1
1
1
1
1
1
1
*
*
*
*
*
*
*
*
*
*
*
BrokerServer
+ConnectionManager connectionManager
+VirtualHostManager virtualHostManager
+ExchangeManager exchangeManager
+QueueManager queueManager
+BindingManager bindingManager
+MessageManager messageManager
+ConsumerManager consumerManager
+RouteManager routeManager
+start()
+stop()
Connection
+string connectionId
+vector channels
+open()
+close()
Channel
+string channelId
+Connection* connection
+declareExchange()
+deleteExchange()
+declareQueue()
+deleteQueue()
+bindQueue()
+unbindQueue()
+basicPublish()
+basicConsume()
+basicAck()
+basicCancel()
VirtualHost
+string vhostName
+map exchanges
+map queues
+map bindings
Exchange
+string name
+enum type
+bool durable
+bool autoDelete
+route(Message msg) : vector
Queue
+string name
+bool durable
+bool exclusive
+bool autoDelete
+queue messages
+push(Message msg)
+pop() : Message
Binding
+string exchangeName
+string queueName
+string bindingKey
Message
+string id
+string routingKey
+map properties
+binary body
Consumer
+string consumerId
+Channel* channel
+string queueName
+deliver(Message msg)

相关推荐
keep intensify4 小时前
MIT 6.824 lab3B/C
分布式·后端·golang
java1234_小锋4 小时前
RabbitMQ中有哪几种交换机类型?
分布式·rabbitmq
代码漫谈4 小时前
探索RabbitMQ集群:如何实现消息的高可用性和负载均衡
分布式·消息队列·rabbitmq·负载均衡
weisian15119 小时前
Java并发编程--45-分布式一致性协议入门:Raft、Paxos与ZAB的核心思想
java·分布式·raft·paxos·zab
juniperhan20 小时前
Flink 系列第17篇:Flink Table&SQL 核心概念、原理与实战详解
大数据·数据仓库·分布式·sql·flink
卢傢蕊1 天前
FastDFS 分布式存储
分布式·fastdfs
菜鸟小码1 天前
Hadoop大数据时代的底座和基石
大数据·hadoop·分布式
珠海西格电力1 天前
零碳园区管理系统如何守护能源与数据安全?
大数据·人工智能·分布式·架构·能源
weisian1511 天前
Java并发编程--44-分布式限流:令牌桶与漏桶算法在网关层的落地
java·分布式·令牌桶算法·漏桶算法·固定窗口算法·滑动窗口算法