【消息队列项目】项目详细设计方案

目录

一.项目模块划分

1.1.服务端模块

1.2.客户端模块

二.服务端模块

2.1.数据管理模块

2.1.1.交换机数据管理模块

2.1.2.队列数据管理模块

2.1.3.绑定数据管理模块

2.1.4.消息数据管理模块

2.2.虚拟机数据管理模块

2.3.路由匹配模块

2.4.消费者管理模块

2.5.通信通道管理模块

2.6.连接管理模块

2.7.服务端BrokerServer模块

三.客户端模块

[3.1. 消费者管理模块](#3.1. 消费者管理模块)

[3.2. 通信通道管理模块](#3.2. 通信通道管理模块)

[3.3. 连接管理模块](#3.3. 连接管理模块)

3.4.异步工作池模块

四.模块关系图


一.项目模块划分

本项目构建一个基于AMQP协议的消息队列系统,由三个独立进程组成:

  1. 服务端:消息队列的核心服务进程,负责处理所有数据存储、路由和投递逻辑。

  2. 发布客户端:生产者进程,用于向服务端发送消息。

  3. 订阅客户端:消费者进程,用于从服务端接收并处理消息。

1.1.服务端模块

1. 数据管理模块

该模块负责管理服务端的核心数据实体,包括以下四个子模块:

  1. 交换机数据管理模块

    负责交换机的增加、删除、修改操作,并提供数据持久化存储功能。

  2. 队列数据管理模块

    负责队列的增加、删除、修改操作,并提供数据持久化存储功能。

  3. 绑定数据管理模块

    负责绑定关系(交换机与队列之间的关联)的增加、删除、修改操作,并提供数据持久化存储功能。

  4. 消息数据管理模块

    负责消息的增加、删除、修改操作,并提供数据持久化存储功能。

2. 虚拟机数据管理模块

该模块是对上述四个数据管理模块(交换机、队列、绑定、消息)的整合与管理。

在消息队列系统中,一个虚拟机通常作为一个逻辑单元,包含完整的交换机、队列、绑定及消息数据,该模块负责协调这些数据的一致性与整体生命周期。

3. 路由匹配模块

负责处理消息的路由逻辑:当新消息发布到交换机时,由该模块根据交换机的类型决定应将其投递到哪些队列。

  • 直接交换机:根据路由键完全匹配投递到对应队列。

  • 广播交换机:将消息投递到所有绑定到该交换机的队列。

  • 主题交换机:根据路由键与绑定键的模式匹配规则进行投递,支持通配符匹配。

该模块的核心功能是执行匹配逻辑,确保消息按规则正确路由。

4. 消费者管理模块

消费者指订阅了某个队列消息的客户端。当队列中有新消息到达时,该模块负责将消息推送给已订阅该队列的消费者。

在实现中,该模块维护消费者与队列的订阅关系,并在消息可投递时通过对应的通信通道向消费者推送消息。

5. 通信通道管理模块

在同一个网络连接中,可以创建多个独立的通信通道,每个通道负责处理一类消息或一个会话。

当某个客户端需要关闭通信时,实际关闭的是对应的通道,而非整个连接。

关闭通道时,该模块负责清理该通道对应的订阅关系及相关资源。

6. 连接管理模块

负责管理与客户端的网络连接。每个连接可能包含多个通信通道,当连接关闭时,需要同时清理该连接下的所有通道及对应的订阅信息。

该模块维护连接与通道之间的对应关系,确保资源正确释放。

7. 服务端BrokerServer模块

该模块是对上述所有模块的整合,对外提供统一的消息队列服务。它负责接收客户端连接、协调各模块协作,并实现消息的接收、存储、路由和投递等完整流程。


1.2.客户端模块

1. 消费者管理模块

在订阅客户端中,当用户订阅某一队列的消息时,即创建一个对应的消费者。该模块负责维护消费者状态,并处理服务端推送过来的消息。

2. 通信通道管理模块

客户端中的通信通道与服务端的通道一一对应,负责在客户端与服务端之间传输数据。客户端通过通道向服务端发送请求,并接收服务端的响应或消息推送。

3. 连接管理模块

客户端通过一个网络连接与服务端通信,该连接可承载多个通道。该模块负责维护连接的建立、保持与关闭,并整合客户端所用资源,为用户提供透明的通信服务。

4. 客户端封装实现

基于上述三个模块,封装为两种客户端类型:

  • 发布客户端

    用户可通过该客户端向指定交换机发布消息。客户端将消息通过通信通道发送至服务端,由服务端进行后续路由与投递。

  • 订阅客户端

    用户可通过该客户端订阅指定队列的消息。当服务端向该队列投递消息时,客户端通过通信通道接收消息,并进行相应处理。

二.服务端模块

2.1.数据管理模块

2.1.1.交换机数据管理模块

1.要管理的数据

该模块负责维护交换机的基本数据信息,每个交换机包含以下属性:

  • 交换机名称:全局唯一标识。

  • 交换机类型:决定消息的转发机制,主要包括以下三种常见类型:

    1. 直接交换 :若消息的 routing_key 与队列绑定的 binding_key 完全一致,则消息被投递至该队列。

    2. 广播交换:消息将被转发至绑定在该交换机上的所有队列。

    3. 主题交换 :根据 routing_keybinding_key 的模式匹配规则进行路由,匹配成功则消息投递至对应队列。

  • 持久化标志:标识该交换机是否需要进行持久化存储。

  • 其他信息......

2. 提供的管理操作

  • 创建交换机:如果指定名称的交换机不存在,则创建新交换机;若已存在,则直接返回。

  • 删除交换机:删除指定交换机。注意:每个交换机都会绑定一个或者多个队列,这就意味着会有一个或者多个的绑定信息,我们删除交换机的时候,我们也需要将它删除。、

  • 获取指定名称的交换机:根据名称查询并返回对应交换机信息。

  • 获取交换机数量:返回当前系统中存在的交换机总数。

2.1.2.队列数据管理模块

1.要管理的数据

  • 队列的名称:唯一标识
  • 持久化存储标志:决定了是否将队列信息持久化存储,也决定了队列里面的消息是否需要持久化
  • 是否独占标志:独占就指的是,只有当前客户端自己才能订阅队列消息,其他客户端不能订阅

2.提供的管理操作

  • 创建队列
  • 删除队列
  • 获取指定队列信息
  • 获取队列数量
  • 获取所有队列的名称:当系统重启后,需要重新加载数据,加载历史信息(消息以队列为单元存储在文件里面,加载消息需要知道队列名称,因为后面消息存储的时候,存储文件以队列名称进行的取名)

一个队列如果持久化标志为false,则意外着重启后,队列没了,也没有客户端能订阅队列的信息,因此这个队列的消息如果持久化存储,是没有意义的,因此应该队列的持久化标志是false,那么它的消息也不需要持久化。

2.1.3.绑定数据管理模块

它的核心任务就是描述一下将哪个队列与哪个交换机绑定到了一起

1.要管理的数据

  • 交换机名称
  • 队列名称
  • Binding Key(绑定键): 这个键用于和消息携带的路由键(Routing Key) 进行匹配,以决定消息是否要投递到该队列。

2.提供的管理操作

  • 添加绑定
  • 解除绑定
  • 获取交换机相关的所有绑定信息(删除交换机的时候,需要删除相应绑定信息;当消息发布到交换机,交换机得通过这些
  • 获取队列的所有绑定信息(删除队列的时候,需要删除相应绑定信息;)
  • 获取绑定信息的数量

2.1.4.消息数据管理模块

  1. 消息属性

每条消息包含以下属性:

  • ID:消息的唯一标识。

  • 持久化标志:标识该消息是否需要进行持久化存储。

  • **routing_key:**用于决定消息应发布到哪个队列。消息发布到交换机后,根据队列绑定的 binding_key 进行路由。

  • 消息主体:消息的实际内容。

  • 存储偏移量:消息按队列为单位存储在文件中,该偏移量表示消息相对于文件起始位置的字节偏移。

  • 消息长度:从偏移量开始读取的字节数,用于解决网络传输或文件读取中的"粘包"问题。

  • 是否有效标志:标记该消息是否已被逻辑删除(例如,被消费确认后标记为无效)。

说明:删除消息时并不立即进行数据搬移,而是仅将标志置为无效。当文件中有效消息占比低于 50%,且消息总数超过 2000 条时,会触发垃圾回收机制,重新整理文件中的数据。系统重启时,也仅重新加载有效消息,相当于执行一次垃圾回收。

垃圾回收:将所有有效消息从文件里面读取到内存,然后清空并截断文件,最后将有效消息重新写入文件中,从而保证文件中仅包含有效数据。

  1. 消息管理信息

管理方式

队列为单元进行管理,所有消息操作都基于队列进行。

管理数据

  • 消息链表:保存当前待推送给消费者的所有消息。

  • 待确认消息哈希表:记录已推送给客户端但尚未收到确认的消息,收到确认后再从该结构中移除并执行持久化删除。

  • 持久化消息哈希表:用于维护已持久化消息的实际存储位置。由于垃圾回收会改变消息在文件中的位置,该结构需要在垃圾回收后更新对应消息的存储偏移量(仅更新已持久化的消息)。

  • 持久化有效消息数量:当前队列中已持久化且未被删除的消息数量。

  • 持久化总消息数量:队列中所有已持久化消息的数量,用于判断是否触发垃圾回收。

  1. 主要操作与方法

队列消息结构管理

  • 初始化队列消息结构:在队列创建时调用,初始化相关数据结构。

  • 移除队列消息结构:在队列删除时调用,清理该队列对应的所有消息数据。

消息生命周期操作

  • 向队列新增消息:将新消息加入消息链表,并视情况持久化。

  • 获取队首消息 :从待发送消息链 表中取出最早的一条消息,并将其移入待确认消息哈希表。

  • 对消息进行确认 :从待确认消息 哈希表中移除指定消息,并删除其在持久化存储中的数据

  • 恢复队列历史消息:通常在系统重启时执行,从持久化文件中加载有效消息到内存结构中。

  • 垃圾回收:由持久化子模块执行,当有效消息数量占比低于 50% 且消息总数超过 2000 时触发,重新整理文件存储。

  • 删除队列相关消息文件:当队列被删除时,清除其对应的所有持久化消息文件。

2.2.虚拟机数据管理模块

该模块是对上述四个数据管理模块(交换机、队列、绑定、消息)的整合与管理。

在消息队列系统中,一个虚拟机通常作为一个逻辑单元,包含完整的交换机、队列、绑定及消息数据,该模块负责协调这些数据的一致性与整体生命周期。

需要管理的数据

  • 交换机数据管理句柄
  • 队列数据管理句柄
  • 绑定信息管理句柄
  • 消息数据管理句柄

需要提供的操作

  • 声明/删除交换机:注意------在删除交换机的时候需要删除相关的绑定信息
  • 声明/删除队列:注意------在删除队列时,需要删除相关的绑定信息以及消息数据
  • 队列的绑定/解除绑定:注意------绑定的时候,必须确认交换机和队列是存在的
  • 获取指定队列的消息
  • 对指定队列的指定消息进行确认
  • 获取交换机相关的所有绑定信息:一条消息要发布给指定交换机的时候,交换机获取所有绑定信息,来确定消息要发布给哪个队列。

2.3.路由匹配模块

这个模块决定了一条消息是否能发布到指定的队列

这里就涉及到两个东西:

Routing Key(路由键)

  • 属于每条消息,由生产者发送消息时指定。

  • 一条消息只有一个 Routing Key。

Binding Key(绑定键)

  • 属于每个绑定,而每个绑定连接一个交换机和一个队列。

  • 一个队列可以有多个绑定 ,因此可以有多个不同的 Binding Key。

  • 一个绑定就是一条规则:"如果消息的 Routing Key 匹配这个 Binding Key,就把消息送到这个队列"。

它们是怎么进行工作的?

现在,我们通过一个消息从生产到消费的完整流程,来理解这些组件如何协同工作,并具体解释 Exchange 和 Queue 的"多对多"关系。

步骤 1:生产者发送

生产者应用创建一个消息,为其指定一个路由键(Routing Key) ,然后将其发送到 Broker 上一个已知的 Exchange

步骤 2:交换机路由

Exchange 收到消息后,会提取消息中的 Routing Key 。然后,它查看所有绑定(Binding) 到自身的规则列表。

步骤 3:绑定匹配与投递

对于每一条绑定规则,Exchange 会根据自身的类型将消息的 Routing Key与绑定的 Binding Key进行匹配。

  • 如果匹配成功,Exchange 就会将消息的一个副本投递到该绑定规则所指向的队列。
  • 如果匹配失败,则跳过该队列。
  • Exchange 会遍历所有绑定到它的规则,可能将消息投递到零个、一个或多个队列。

步骤 4:队列存储与消费

  • 消息被投递到队列后,便存储在队列中。消费者应用从自己订阅的队列(中获取消息进行处理。

那么它们怎么进行匹配呢?

交换机的类型决定了Binding Key和Routing Key的匹配算法。

1. 直连交换机(Direct)

  • 匹配规则:精确、完全相等的字符串匹配。
  • Binding Key:必须是一个明确的字符串,如 "email"、"order.paid"。
  • Routing Key:必须与Binding Key完全一致,消息才会被路由。

示例:

  • 队列Q1绑定了 Binding Key: "error"。
  • 生产者发送消息A (Routing Key: "info") -> 不匹配,消息A不会进入Q1。
  • 生产者发送消息B (Routing Key: "error") -> 精确匹配,消息B进入Q1。

用途:点对点精确路由,常用于任务分发(如将不同的任务类型路由到不同的处理队列)。


2. 主题交换机(Topic)

匹配规则:通配符模式匹配。这是最灵活、最常用的路由方式。

Binding Key:是一个用点号.分隔的单词组成的模式,支持两个通配符:

  • * (星号):匹配恰好一个单词。
  • (井号):匹配零个或多个单词。

Routing Key:也是一个用点号.分隔的单词组成的字符串(不能包含通配符)。

示例:

  • 队列Q1绑定了 Binding Key: "*.stock.usd" -> 关心所有以.stock.usd结尾,且中间有一个任意单词的消息。
  • 队列Q2绑定了 Binding Key: "nyse.#" -> 关心所有以nyse.开头的消息。
  • 生产者发送消息 (Routing Key: "nyse.stock.usd") -> 同时匹配Q1和Q2,消息会进入两个队列(广播)。
  • 生产者发送消息 (Routing Key: "forex.eur.usd") -> 只匹配Q1,消息进入Q1。
  • 生产者发送消息 (Routing Key: "nyse") -> 只匹配Q2(#可以匹配零个单词),消息进入Q2。

用途:基于多重标准(如来源、严重级别、业务类型)的灵活消息路由,如日志系统、事件通知系统。


3. 扇出交换机(Fanout)

  • 匹配规则:忽略Routing Key和Binding Key。
  • 行为:它会将收到的所有消息无条件地广播到所有与之绑定的队列。
  • Binding Key:在创建绑定时通常设置为空字符串""(但设置什么值都无所谓,因为不会被使用)。
  • 用途:纯粹的广播/发布-订阅场景。

设计路由匹配模块

在这个模块里,本质没有需要管理的数据,只有向外提供的路由匹配操作

  • 判断Binding Key和Routing Key是否成功匹配的接口
  • 判断Routing Key是否符合我们的约定(只能由数字,字母,_ 构成)
  • 判断Binding Key是否符合我们的规定(只能由数字,字母,_,*,#构成;注意:* (星号):匹配恰好一个单词;# (井号):匹配零个或多个单词)

2.4.消费者管理模块

本模块用于管理消息队列中的消费者(客户端)。系统中存在两种客户端角色:

  • 发布消息客户端:负责向队列发送消息。

  • 订阅消息客户端:负责从队列接收消息,仅此类客户端被视为消费者。

消费者数据的核心作用是:当其所订阅的队列有新消息时,系统能够根据消费者信息(尤其是网络连接)将消息准确推送给对应的客户端。消费者在接收消息后,需向Broker确认消息处理状态。

  1. 消费者信息

每个消费者包含以下关键属性:

属性 说明
消费者标识(tag) 消费者的唯一标识符
订阅队列名称 所订阅的队列,用于消息路由与确认;当当前队列有消息,就会将消息推送给客户端;客户端收到消息后,需要对指定队列的消息进行确认
自动确认标志 当消费者从队列中获取消息后,必须向Broker告知该消息的最终处理状态。 控制消息确认模式: • 自动确认 :一旦Broker将消息通过网络投递给消费者,便立即将该消息从队列中标记为删除或直接删除。 • 手动确认 :消费者在成功处理完消息后,必须显式向Broker发送一个确认信号。Broker在收到此确认后,才会将消息从队列中安全地移除。
消费处理回调函数指针 当队列有消息时,调用此函数向对应客户端推送消息(内部逻辑固定)
  1. 管理设计思想

消费者管理以队列为单元进行组织:

  • 每个消费者只订阅特定的队列。

  • 消息确认也以队列为基础进行。

  • 当某个队列有消息到达时,系统从该队列的消费者列表中选取一个消费者 进行推送(默认采用RR轮转策略)。

  1. 队列消费者管理结构

数据成员

  • 消费者链表:保存当前队列的所有消费者信息。消息推送时,采用RR轮转方式依次选取下一个消费者,确保每条消息仅由一个消费者处理。

成员方法(管理操作)

  1. 初始化队列消费者结构

  2. 删除队列消费者结构

  3. 向指定队列添加消费者

  4. 通过RR轮转获取下一个消费者

  5. 删除指定队列中的消费者

  6. 获取指定队列的消费者总数

  7. 判断指定队列的消费者列表是否为空

5.关键操作说明

操作 功能
新增消费者 将消费者加入对应队列的链表
RR轮转获取消费者 从链表按轮转策略选取下一个消费者
删除消费者 从队列链表中移除指定消费者
队列消费者总数 返回当前队列的消费者数量
队列是否为空 判断当前队列是否有活跃消费者

2.5.通信通道管理模块

通信通道,也叫信道

网络通信的时候,必然是通过网络通信连接来完成的。

为了能充分的利用资源,因此对通信连接有进行了进一步的细化,细化出了通信通道,对于用户来说,一个通信通道,就是进行网络通信的载体,而一个真正的通信连接,可以创建出多个通信通道

每一个信道之间,在用户的眼中是是相互独立的,但是在本质的底层中它们使用同一个通信连接进行网络通信

因此,因为信道是用户眼中的一个通信通道,所以所有的网络通信服务都是由通信通道通过的。

信道提供的服务操作

  1. 声明/删除交换机
  2. 声明/删除队列
  3. 绑定/解除绑定队列与交换机
  4. 订阅队列消息/取消订阅队列消息
  5. 发布消息/队列消息确认

信道需要管理的消息

  • 信道ID
  • 信道关联的虚拟机
  • 信道关联的消费者句柄:当信道关闭的时候,所有关联的消费者订阅都需要取消,相当于删除所有的相关消费者
  • 工作线程池句柄 :信道进行了消息发布到指定队列操作,从指定队列获取一个消费者,对这条消息进行消费,将这条消息推送一个客户端的操作交给线程池执行,这个过程必须使用新线程来完成; 并非每个信道都有一个线程池,而是整个服务器有一个线程池,大家所有的信道都是通过一个线程池进行异步操作而已

信道管理

  • 创建一个信道
  • 关闭一个信道
  • 根据信道ID获取对应的信道句柄

2.6.连接管理模块

其实就是一个网络通信连接

在这里我们使用Muduo库来实现底层通信,Muduo库本身就有Connection连接的概念和对象类

但是我们的连接里面,还有一个上层通信通道的概念,这个概念是Muduo库里面是没有的

我们需要对Muduo库里面的Connection连接进行二次封装

管理数据

  • Muduo库的通信连接
  • 当前连接关联的信道关联句柄

管理的操作

  • 创建信道
  • 关闭信道
  • 新增连接
  • 关闭连接
  • 获取指定连接信息

2.7.服务端BrokerServer模块

整合以上所有模块,并搭建网络通信服务器,实现与客户端网络通信,能够识别客户端请求,并提供客户端请求的处理服务。

管理信息:

  • 虚拟机管理模块句柄
  • 消费者管理模块句柄
  • 连接管理模块句柄
  • 工作线程池句柄
  • muduo库通信所需元素...

这个模块其实功能整合模块,并不提供实质的功能性操作

这个模块最重要的是资源的整合,是一个资源的载体

  • 一个服务器有一个工作线程池,其他所有的信道操作都是这同一个线程池
  • 一个服务器有一个虚拟机,其他所有交换机,队列,绑定,消息的操作都是针对这个虚拟机进行的
  • 一个服务器有一个消费者管理,
  • 通信相关连接管理,协议处理模块句柄,也是一整个服务器有一套

三.客户端模块

3.1. 消费者管理模块

在订阅客户端中,当用户订阅某一队列的消息时,即创建一个对应的消费者。该模块负责维护消费者状态,并处理服务端推送过来的消息。

管理的信息

  • 消费者标识
  • 订阅的队列名称
  • 自动确认标志
  • 消息回调处理函数指针

当当前消费者订阅了某个队列的下线,这个队列有消息后就会将消息推送给客户端,这时候收到了消息则使用回调函数进行处理,处理完毕后根据自动确认标志决定是否进行消息确认

管理操作

  • 新增消费者
  • 删除消费者
  • 查询当前消费者的信息

3.2. 通信通道管理模块

客户端中的通信通道与服务端的通道一一对应,负责在客户端与服务端之间传输数据。客户端通过通道向服务端发送请求,并接收服务端的响应或消息推送。

所有提供的操作与服务器雷同,因为客户端要给用户提供什么服务,服务器就要给客户端提供什么服务

管理信息

  • 消费者管理句柄:每个信道都有自己相关的消费者
  • 线程池句柄:对推送过来的消息进行回调处理,处理过程由工作线程来进行
  • 信道关联的连接

信道提供的服务

  1. 声明/删除交换机
  2. 声明/删除队列
  3. 绑定/解除绑定队列与交换机
  4. 订阅队列消息/取消订阅队列消息
  5. 发布消息/队列消息确认
  6. 创建信道/关闭信道

信道的关联

  • 信道的增删查

3.3. 连接管理模块

客户端通过一个网络连接与服务端通信,该连接可承载多个通道。该模块负责维护连接的建立、保持与关闭,并整合客户端所用资源,为用户提供透明的通信服务。

客户端连接的管理,本质上是对Muduo库里面的客户端TcpClient的二次封装

面对用户,不需要有客户端的概念,连接对于用户来说就是客户端,提供连接创建信道,提供信道完成自己所需服务

因此,当前客户端连接对用户来说就是一个资源的载体

管理操作:

  • 连接服务器
  • 创建信道
  • 关闭信道
  • 关闭连接

管理的资源

  • 工作线程池
  • 连接关联的信道关联句柄

3.4.异步工作池模块

其实这里面就2个东西

  1. Muduo库里面的客户端TcpClient模块需要一个EventLoopThread模块进行IO事件监控
  2. 收到推送消息后,需要对推送过来的消息进行处理,因此需要一个线程池来帮助我们完成消息的处理过程

将异步工作池模块独立出来的原因:

  • 多个连接用一个EventLoopThread模块进行IO事件监控就够了
  • 所有的推送消息处理只需要一个线程池就够了
  • 并不需要每个连接都有一个EventLoop,也不需要每个信道的消息处理都有自己的线程池

四.模块关系图

接下来我们看另外一幅

还是很清晰明了的吧

相关推荐
gaize121310 小时前
腾讯云锐驰和蜂驰的区别
服务器·腾讯云
gxh199210 小时前
4步将HTTP请求升级为HTTPS
运维·服务器·网络协议·http·https
云和数据.ChenGuang10 小时前
运维工程师技术之nfs共享文件系统
运维·服务器·运维技术·数据库运维工程师·运维教程
ITMr.罗10 小时前
深入理解EF Core更新机制(开发中因为省事遇到的问题)
服务器·数据库·c#·.net
❀͜͡傀儡师11 小时前
Docker部署Rustscan端口扫描工具
运维·docker·容器
暴风游侠11 小时前
linux知识点-服务相关
linux·服务器·笔记
JANG102411 小时前
【Linux】常用指令
linux·服务器·javascript
feng_blog668811 小时前
cursor通过ssh连接远程服务器
运维·服务器·ssh
秋刀鱼 ..11 小时前
第二届光电科学与智能传感国际学术会议(ICOIS 2026)
运维·人工智能·科技·机器学习·制造
蓝天~白云11 小时前
ESXI虚拟机启动卡住在0%,无法关闭
linux·运维·服务器