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

目录

一.项目模块划分

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,也不需要每个信道的消息处理都有自己的线程池

四.模块关系图

接下来我们看另外一幅

还是很清晰明了的吧

相关推荐
SelectDB17 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
zzzzzz3102 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode2 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220703 天前
如何搭建本地yum源(上)
运维
大树886 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质6 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz6 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工6 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智6 天前
ARP代理--工作原理
运维·网络·arp·arp代理