消息中间件进阶:RocketMQ 消息的生产与发送(二)

系列第三篇:存储与原理篇(中)

你好,我们又见面了。

上一篇文章我们钻进了 RocketMQ 的"肚子里",把存储机制翻了个底朝天------CommitLog、ConsumeQueue、零拷贝、刷盘策略......这些底层原理我们都聊透了。但有个问题你可能一直在想:消息到底是怎么从 Producer 发送到 Broker 的?

打个比方:上一篇文章我们搞清楚了"仓库"(CommitLog)是怎么建成的、货架(ConsumeQueue)是怎么摆放的。但货是怎么从"发货方"(Producer)运到"仓库"的?路上走哪条路?堵车了怎么办?丢件了怎么处理?

今天这篇文章,我们就把消息发送的全链路彻底讲透。从 Producer 启动的那一刻开始,到消息成功写入 Broker 并返回响应,每一个环节我们都不放过。老规矩,配合流程图,咱们一步一图。

六、消息的生产与发送

Producer 启动流程与路由获取

我们先从 Producer 的启动开始。一个 Producer 启动的时候,它可不是傻乎乎地直接就开始发消息了------它得先弄清楚"把消息发到哪里去"。

Producer 启动的核心步骤
flowchart TD Start(创建 Producer 实例) --> Step11. 设置 ProducerGroup 名称\(同一个业务使用同一个 Group) Step1 --> Step22. 设置 NameServer 地址列表\(指定集群注册中心的地址) Step2 --> Step33. 配置其他参数\(发送超时时间、重试次数等) Step3 --> Step44. 调用 start 方法启动 Producer Step4 --> Step55. 初始化 MQClientInstance\(客户端核心实例,负责网络通信和路由管理) Step5 --> Step66. 启动 Netty 通信客户端\(建立与 NameServer 的连接) Step6 --> Step77. 从 NameServer 拉取所有 Topic 的路由信息 Step7 --> Step88. 启动定时任务\(每 30 秒更新一次路由信息) Step8 --> End(Producer 就绪,等待发送消息) style Step6 fill:#e3f2fd style Step7 fill:#fff3e0 style Step8 fill:#c8e6c9

关键点解析

  1. ProducerGroup:一个业务标识,同一个业务里的 Producer 实例归属于同一个 Group。在事务消息中,同一个 Group 的 Producer 可以互相回查事务状态。

  2. MQClientInstance :这是客户端最核心的实例,所有 Producer 和 Consumer 共用一个 MQClientInstance(同一个 JVM 里)。它负责:

    • 管理与 NameServer 的连接
    • 管理与 Broker 的连接
    • 维护本地路由缓存
    • 统一的心跳和网络通信
  3. 路由获取 :Producer 启动时,会从 NameServer 拉取所有 Topic 的路由信息(不仅仅是某个特定 Topic),这样后续发消息时就不需要再等路由查询了。

  4. 定时更新 :路由信息在本地缓存后,会有一个定时任务每隔 30 秒 从 NameServer 拉取最新路由,保证路由信息的时效性。

消息发送的三种方式:同步、异步、单向

在《入门认知篇》里我们简单提过三种发送方式,现在我们从底层实现的角度再来看一遍。

1. 同步发送(Sync)

这是最常用、最简单的方式。发送消息后,线程阻塞等待 Broker 返回响应,收到响应后才继续执行。

java 复制代码
SendResult sendResult = producer.send(msg);
// 阻塞等待,直到收到 Broker 的响应

sequenceDiagram participant P as Producer participant B as Broker P->>P: 1. 构造消息 P->>B: 2. 发送请求(同步) Note over P: 线程阻塞等待 B-->>P: 3. 返回响应(成功/失败) Note over P: 线程恢复 P->>P: 4. 根据结果处理业务逻辑 Note over P: ⏱️ 整个过程中线程处于阻塞状态

适用场景:关键业务------比如下单成功后的订单消息,必须确认 Broker 收到了才能继续。

2. 异步发送(Async)

发送消息后,线程不阻塞 ,立即返回。等 Broker 响应回来后,通过回调函数来处理结果。

java 复制代码
producer.sendAsync(msg, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        // 处理成功逻辑
    }
    @Override
    public void onException(Throwable e) {
        // 处理失败逻辑
    }
});
// 这里立即返回,不阻塞

sequenceDiagram participant P as Producer 主线程 participant T as Producer 异步线程 participant B as Broker P->>P: 1. 构造消息 P->>T: 2. 提交异步发送任务 Note over P: 主线程立即返回<br>可以继续其他工作 T->>B: 3. 发送请求 B-->>T: 4. 返回响应 T->>T: 5. 回调 SendCallback Note over P: ✅ 主线程未被阻塞<br>⏱️ 响应处理在异步线程中

适用场景:对延迟敏感但需要知道结果------比如前端请求发消息,不能阻塞用户操作,但失败时需要通知用户。

3. 单向发送(Oneway)

只发不管,连响应都不等。最轻量、最快,但最不可靠。

java 复制代码
producer.sendOneway(msg);
// 不管结果,直接继续

sequenceDiagram participant P as Producer participant B as Broker P->>P: 1. 构造消息 P->>B: 2. 发送请求(单向) Note over P: 发完就走<br>不等待任何响应 P->>P: 3. 继续执行后续代码 Note over P: 🚀 最快,但没有任何可靠性保证

适用场景:日志上报、监控数据等------丢了就丢了,业务上能接受。

💡 小贴士 :同步发送和异步发送虽然看起来差异很大,但在底层,它们都使用了异步网络通信 (Netty)。同步发送只是在异步通信的基础上,用 CountDownLatch 等同步工具做了一个"阻塞等待"的封装。本质上,RocketMQ 的网络通信模型是全异步的。

消息发送的负载均衡策略

当一个 Topic 有多个 MessageQueue(分布在不同的 Broker 上)时,Producer 如何选择将消息发到哪个 Queue?这就涉及负载均衡策略

RocketMQ 默认提供了两种内置策略,你也可以自定义实现:

策略一:轮询(Round Robin)------ 默认策略

依次轮流选择 Queue,确保消息均匀分布。在顺序消息场景下,会结合消息 Key 做哈希,保证同一个 Key 的消息落到同一个 Queue。
flowchart LR subgraph Messages消息序列 M1消息 1 M2消息 2 M3消息 3 M4消息 4 M5消息 5 M6消息 6 end subgraph QueuesMessageQueue Q1Queue 0 Q2Queue 1 Q3Queue 2 end M1 -->|第 1 条| Q1 M2 -->|第 2 条| Q2 M3 -->|第 3 条| Q3 M4 -->|第 4 条| Q1 M5 -->|第 5 条| Q2 M6 -->|第 6 条| Q3 Note1按顺序轮流分配:1→Q1, 2→Q2, 3→Q3, 4→Q1...

策略二:一致性哈希(Consistent Hash)

根据消息 Key 的哈希值,通过哈希环来决定消息发到哪个 Queue。保证同一个 Key 的消息始终落在同一个 Queue 上(对于顺序消息特别重要)。
flowchart TB subgraph HashRing一致性哈希环 direction LR N1节点 1\Queue 0 N2节点 2\Queue 1 N3节点 3\Queue 2 N4节点 4\Queue 3 N5节点 5\Queue 0 N6节点 6\Queue 1 end subgraph Keys消息 Key K1order_123 K2order_456 K3order_789 end K1 -->|哈希值映射到环上| K1Pos位置 A K1Pos -->|顺时针查找第一个节点| N1 K2 -->|哈希值映射到环上| K2Pos位置 B K2Pos -->|顺时针查找第一个节点| N3 K3 -->|哈希值映射到环上| K3Pos位置 C K3Pos -->|顺时针查找第一个节点| N5 NoteHash同一 Key 始终落到同一 Queue\新增节点时只影响局部 Key 的重新分配

自定义策略 :实现 MessageQueueSelector 接口,根据自己的业务逻辑选择 Queue(比如按订单 ID 取模)。

消息发送的重试机制与故障规避

消息发送不是总能一次成功的。网络抖动、Broker 故障、磁盘满了......各种原因都可能导致发送失败。RocketMQ 有一套完善的重试和故障规避机制

重试机制
flowchart TD Start(发送消息) --> Try1第 1 次尝试 Try1 --> Check1{成功?} Check1 -->|是| Success(返回成功) Check1 -->|否| Check2{是否为<br>可重试的异常?} Check2 -->|否| Fail(直接返回失败\不重试) Check2 -->|是| Try2第 2 次尝试\等待 50ms Try2 --> Check3{成功?} Check3 -->|是| Success Check3 -->|否| TryN... 最多重试 retryTimesWhenSendFailed 次 TryN --> CheckN{最后一次成功?} CheckN -->|是| Success CheckN -->|否| Fail style Success fill:#c8e6c9 style Fail fill:#ffcdd2

重试的关键配置

参数 默认值 说明
retryTimesWhenSendFailed 2 同步发送失败时的重试次数(不含第一次)
retryTimesWhenSendAsyncFailed 2 异步发送失败时的重试次数(不含第一次)
retryAnotherBrokerWhenNotStoreOK false 同步模式下,如果写入失败是否换 Broker 重试

哪些异常会触发重试?

  • 可重试 :网络超时、连接异常、Broker 返回 SYSTEM_BUSYSERVICE_NOT_AVAILABLE
  • 不重试:消息体超限(4MB)、Topic 不存在、消息格式错误等(重试也没用)

故障规避机制(故障延迟机制)

这是 RocketMQ 一个非常巧妙的设计。它的核心思想是:某个 Broker 如果发送失败了,在接下来的一段时间内,尽量不要再往这个 Broker 上发消息
flowchart LR subgraph Normal正常情况 P1Producer --> B1Broker A P1 --> B2Broker B P1 --> B3Broker C end subgraph FailureBroker B 故障后 P2Producer -->|不发送| B2fBroker B ❌ P2 --> B1fBroker A ✅ P2 --> B3fBroker C ✅ NoteFault在故障延迟时间内\Broker B 被加入黑名单\自动规避 end style B2 fill:#ffcdd2 style B2f fill:#ffcdd2 style NoteFault fill:#fff3e0

具体工作方式

  1. 每次发送失败时,记录当前系统时间和失败的 Broker
  2. 在接下来的 sendLatencyFaultEnable 时间内(默认 30 秒),该 Broker 会被加入黑名单
  3. Producer 选择 Queue 时,会过滤掉黑名单中的 Broker
  4. 30 秒后自动恢复,重新尝试往该 Broker 发送

这个机制可以避免 Producer 持续向故障 Broker 发送消息,从而减少不必要的超时等待,提升整体的发送成功率。

消息的路由队列选择算法

消息从 Producer 到 Broker 的过程,实际上经历了两层选择:
flowchart TD Start(需要发送消息) --> Step11. 根据 Topic 获取本地缓存的路由信息 Step1 --> Step22. 从路由信息中获取所有可用的\MessageQueue 列表 Step2 --> Step33. 过滤掉\处于故障延迟期的 Broker 上的 Queue Step3 --> Step44. 根据负载均衡策略\选择一个 MessageQueue Step4 --> Step55. 根据选中的 Queue 获取\对应的 Broker 地址 Step5 --> Step66. 构造消息发送请求\通过 Netty 发送到目标 Broker Step6 --> End(等待响应) style Step2 fill:#fff3e0 style Step4 fill:#e3f2fd style Step6 fill:#c8e6c9

完整的选择决策流程包含以下因素

  1. 路由信息:从本地缓存中获取 Topic 对应的所有 Broker 和 Queue 信息
  2. Broker 可用性:过滤掉 NameServer 已剔除的、或故障延迟期内的 Broker
  3. 负载均衡策略:轮询或一致性哈希
  4. Queue 状态:如果某个 Queue 满了,可能被临时剔除(虽然 RocketMQ 很少出现这种情况,因为文件是滚动的)

消息钩子(MessageHook)的使用

MessageHook 是什么?

MessageHook 是 RocketMQ 提供的一个扩展点 ,允许开发者在消息发送的前后插入自定义逻辑。
flowchart LR subgraph SendHook消息发送钩子 Before发送前\executeBefore --> Send消息发送 Send --> After发送后\executeAfter end subgraph UseCase典型用途 U1📊 链路追踪\埋点记录耗时 U2🔐 权限校验\检查是否有发送权限 U3📈 流量染色\记录调用方信息 U4📝 审计日志\记录消息发送记录 end Before -.-> U1 Before -.-> U2 After -.-> U3 After -.-> U4 style Before fill:#e3f2fd style After fill:#c8e6c9

使用方式

java 复制代码
// 实现 MessageHook 接口
public class CustomSendHook implements MessageHook {
    @Override
    public String hookName() {
        return "CustomSendHook";
    }
    
    @Override
    public void executeBefore(SendMessageContext context) {
        // 发送前:记录开始时间、校验权限等
        System.out.println("开始发送,Topic: " + context.getMessage().getTopic());
    }
    
    @Override
    public void executeAfter(SendMessageContext context) {
        // 发送后:记录耗时、审计日志等
        System.out.println("发送结束,耗时: " + context.getCostTime());
    }
}

// 注册钩子
producer.getDefaultMQProducerImpl().registerMessageHook(new CustomSendHook());

典型应用场景

  • 全链路追踪:在发送前生成 TraceId,发送后记录日志
  • 性能监控:统计消息发送的耗时分布
  • 流量控制:在发送前进行限流或权限校验

消息发送的超时处理与异常场景

消息发送过程中可能遇到各种异常,我们来看 RcoketMQ 的异常处理是如何设计的:

超时处理

同步发送有一个超时时间(默认 3 秒)。如果在这个时间内没收到 Broker 的响应,Producer 会抛出 RemotingTimeoutException
flowchart LR PProducer -->|发送请求| BBroker P -->|启动超时计时器| TTimer 3s T -->|未超时| Wait等待响应 Wait -->|收到响应| Success✅ 返回成功 Wait -->|3秒到了| Timeout❌ 抛出超时异常 style Timeout fill:#ffcdd2 style Success fill:#c8e6c9

⚠️ 注意 :超时异常发生时,你无法确定 Broker 是否收到了消息 。可能消息根本没到 Broker,也可能 Broker 已经写入了只是网络响应慢了。所以,遇到超时异常时,业务方需要根据消息 Key 做幂等处理,避免重复消费。

常见异常场景

异常类型 可能原因 是否重试 处理建议
RemotingTimeoutException 网络慢或 Broker 响应慢 ✅ 是 适当增大超时时间,或考虑异步发送
RemotingConnectException Broker 连接不上 ✅ 是 检查 Broker 是否存活,故障规避机制会处理
MQClientException Topic 不存在或消息体超限 ❌ 否 检查 Topic 配置和消息大小
MQBrokerException Broker 返回业务错误 看情况 根据错误码判断是否可重试
RemotingSendRequestException 网络发送失败 ✅ 是 检查网络连接

消息发送的请求响应流程(Netty 通信)

RocketMQ 的底层网络通信基于 Netty 实现。一条消息从 Producer 到 Broker,经过的请求响应流程是标准化的:
sequenceDiagram participant P as Producer participant PC as Netty Client participant BC as Netty Server<br>(Broker 端口 10911) participant H as Broker 业务处理器 participant S as 存储层 P->>PC: 1. 调用发送 API PC->>BC: 2. 通过 Netty Channel 发送<br>请求码: SEND_MESSAGE Note over BC: Broker 接收请求 BC->>H: 3. 根据请求码路由到<br>SendMessageProcessor H->>H: 4. 解析请求头<br>(Topic、Queue、消息体等) H->>S: 5. 调用存储层写入 CommitLog S-->>H: 6. 返回写入结果和偏移量 H->>H: 7. 构造响应体 H->>BC: 8. 通过 Netty 返回响应 BC->>PC: 9. 响应返回客户端 PC->>P: 10. 回调/唤醒等待线程 P->>P: 11. 处理发送结果

关键设计细节

  1. 请求码路由 :Netty 收到请求后,根据请求码(SEND_MESSAGEPULL_MESSAGE 等)将请求分发到不同的处理器
  2. 单线程模型 :RocketMQ 的 Broker 使用单线程 处理消息写入(通过 putMessageLock),保证 CommitLog 的顺序写入
  3. 异步化:Broker 处理完写入后,通过 Netty 的异步回调机制返回响应,不阻塞 Netty 的 I/O 线程
  4. 超时控制 :客户端的超时是基于 Netty 的 ResponseFuture 机制实现的

批量消息发送的实现与限制

批量消息发送允许将多条消息打包成一条网络请求发送,从而显著提升吞吐量。
flowchart LR subgraph Single单条发送 S1消息1 -->|请求1| BBroker S2消息2 -->|请求2| B S3消息3 -->|请求3| B NoteSingle3 次网络请求 end subgraph Batch批量发送 B1消息1 --> BatchMsg批量消息\消息1+消息2+消息3 B2消息2 --> BatchMsg B3消息3 --> BatchMsg BatchMsg -->|1 次请求| B4Broker NoteBatch1 次网络请求 end style Single fill:#ffcdd2 style Batch fill:#c8e6c9

批量发送的使用限制

  1. 同一 Topic:批量消息必须属于同一个 Topic
  2. 同一 Broker:批量消息的 Queue 列表必须落在同一个 Broker 上(因为一次网络请求只能发给一个 Broker)
  3. 大小限制 :批量消息的总大小不能超过 maxMessageSize(默认 4MB)
  4. 不能有延迟/事务:批量消息不支持延迟消息和事务消息

使用示例

java 复制代码
List<Message> messages = new ArrayList<>();
messages.add(new Message("topic_test", "消息1".getBytes()));
messages.add(new Message("topic_test", "消息2".getBytes()));
messages.add(new Message("topic_test", "消息3".getBytes()));

SendResult sendResult = producer.send(messages);  // 批量发送

延迟消息的实现原理(延迟等级)

RocketMQ 的延迟消息非常有意思------它不是精确到秒的任意延迟 ,而是基于预定义的延迟等级来实现的。

延迟等级

RocketMQ 内置了 18 个延迟等级(messageDelayLevel),每个等级对应一个固定的延迟时间:

等级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
延迟 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

底层实现原理
flowchart TB subgraph Send发送延迟消息 PProducer -->|设置 delayTimeLevel=3| BBroker end subgraph Storage存储层 B -->|1. 计算投递时间| Calc投递时间 = 当前时间 + 10s Calc -->|2. 消息写入 CommitLog| CLCommitLog CL -->|3. 异步构建索引| CQConsumeQueue end subgraph Delay延迟消息处理 CQ -->|4. 写入 SCHEDULE_TOPIC_XXXX| DL延迟 Topic 的 Queue\(按延迟等级分队列) DL -->|5. 定时任务扫描| Timer后台定时任务\每秒扫描一次 Timer -->|6. 到达投递时间| Resend重新投递到目标 Topic Resend -->|7. 消息被 Consumer 拉取| ConsumerConsumer 消费 end style DL fill:#fff3e0 style Timer fill:#e1bee7 style Resend fill:#c8e6c9

实现逻辑(原理层面)

  1. Producer 发送消息时,设置 delayTimeLevel 参数(比如 3 表示延迟 10 秒)
  2. Broker 收到消息后,不直接把消息写入目标 Topic 的 ConsumeQueue,而是先写入一个内部的 SCHEDULE_TOPIC_XXXX(这个 Topic 有 18 个 Queue,对应 18 个延迟等级)
  3. Broker 有一个后台定时任务ScheduleMessageService),每秒扫描一次,检查哪些延迟消息到了投递时间
  4. 到时间的消息被重新写入目标 Topic 的 CommitLog 和 ConsumeQueue
  5. Consumer 正常消费------对消费者来说,这个消息就像是刚刚发送的一样

💡 小贴士 :这种设计的核心是------延迟消息没有特殊的存储格式,就是普通消息加上一个"定时投递"的机制。所有的延迟逻辑都在 Broker 内部完成,Consumer 完全无感知。所以如果你需要更灵活的定时精度,也可以基于这个机制自行扩展。

5.x 版本的变化

RocketMQ 5.x 对延迟消息做了增强,支持 任意时间点的定时消息 (不再是只有 18 个等级),通过 Timing Wheel 算法实现了更灵活的定时投递。但核心原理和上面的流程是一样的,只是内部的定时机制从"固定等级扫描"升级到了"任意时间点调度"。

消息轨迹(Message Trace)的开启与使用

消息轨迹是什么?

消息轨迹记录了一条消息从生产到消费的完整链路信息。你可以通过消息轨迹,清楚地知道:

  • 这条消息是谁发的、什么时候发的、发到了哪个 Broker
  • 这条消息被谁消费了、什么时候消费的、消费结果如何

flowchart LR subgraph Trace消息轨迹 AProducer 发送 --> BBroker 存储 B --> CConsumer 消费 end subgraph TraceData轨迹数据 TD1发送时间 TD2发送方 IP TD3消息 Key TD4存储位置 TD5消费时间 TD6消费方 IP TD7消费状态 end A -.-> TD1 A -.-> TD2 A -.-> TD3 B -.-> TD4 C -.-> TD5 C -.-> TD6 C -.-> TD7 style Trace fill:#e3f2fd

开启方式

方式一:通过配置文件开启(推荐)

broker.conf 中配置:

复制代码
traceTopicEnable=true
msgTraceTopicName=RMQ_SYS_TRACE_TOPIC

方式二:通过客户端显式开启

java 复制代码
// Producer 端开启消息轨迹
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setEnableMsgTrace(true);
producer.setMsgTraceTopic("RMQ_SYS_TRACE_TOPIC");

消息轨迹的实现原理
flowchart TD Start(消息发送/消费) --> Hook通过 MessageHook 和 ConsumeHook\拦截关键事件 Hook --> Build构建轨迹数据\(发送时间、IP、状态等) Build --> SendTrace将轨迹数据作为\普通消息发送到 Trace Topic SendTrace --> StoreBroker 存储轨迹消息 Store --> Query用户通过控制台或 API\查询轨迹信息 style Hook fill:#e1bee7 style Query fill:#c8e6c9

核心设计

  1. 无侵入:通过 Hook 机制实现,业务代码不需要改造
  2. 异步上报:轨迹数据的发送是异步的,不影响主消息发送的性能
  3. 独立存储 :轨迹数据存储在独立的 RMQ_SYS_TRACE_TOPIC 中,与业务数据隔离
  4. 可查询:通过 RocketMQ 的 Dashboard 或 API 可以按消息 ID/Key 查询轨迹

实际使用中的注意事项

  • 开启消息轨迹会增加少量性能开销(需要额外发送轨迹消息),生产环境根据实际需求开启
  • 轨迹消息本身也占用存储空间,需要适当设置 fileReservedTime
  • 对于超高吞吐量的场景,可以采样记录轨迹,而不是每条都记录

小结

这篇文章我们完整走通了消息从生产到发送的全链路,通过 8 张流程图,搞清楚了:

  • Producer 启动时如何初始化并获取路由信息
  • 三种发送方式(同步、异步、单向)的底层实现和适用场景
  • 负载均衡策略(轮询、一致性哈希)如何选择目标 Queue
  • 重试机制故障规避机制如何在发送失败时保证高可用
  • 路由队列选择的完整流程
  • MessageHook 如何提供灵活的扩展点
  • 超时处理异常场景的处理方式
  • Netty 通信的请求响应全流程
  • 批量发送的优势和限制
  • 延迟消息基于延迟等级的实现原理
  • 消息轨迹如何实现全链路追踪

现在,你应该对 RocketMQ 消息发送的每个环节都了如指掌了。下一篇,我们将从"发送端"切换到"消费端",看看消息到达 Broker 之后,Consumer 是怎么拉取和消费的,消费进度是怎么管理的,消息重试和死信又是怎么处理的。我们下期见!


系列文章:

  1. 入门认知篇 ✅
  2. 核心概念与架构篇 ✅
  3. 存储与原理篇(上)✅
  4. 存储与原理篇(中)✅(本文)
  5. 消息消费与进阶篇(待续)
  6. ......
相关推荐
IT界的老黄牛1 个月前
RocketMQ 4.x 任意秒数延迟消息工程实战:MQ 粗延迟 + Redis 补精度 + MDC 链路透传
redis·rocketmq·事务消息·延迟消息
Java码农也是农1 年前
RabbitMQ使用延迟消息
java·rabbitmq·延迟消息
银氨溶液1 年前
RabbitMQ实现延迟消息发送——实战篇
java·spring boot·分布式·后端·职场和发展·rabbitmq·延迟消息
碳烤小肥羊。。。2 年前
RabbitMQ高级篇,进阶内容
延迟消息·rabblitmq·发送者的可靠性·mq的可靠性·消费者的可靠性
谢小涛2 年前
Spring Boot集成Redisson实现延迟队列
spring boot·redis·redisson·延迟队列·延迟消息
勉之~3 年前
RabbitMQ之延迟消息实战
rabbitmq·延迟消息
求知.3 年前
【RabbitMQ】 RabbitMQ 消息的延迟 —— 深入探索 RabbitMQ 的死信交换机,消息的 TTL 以及延迟队列
rabbitmq·延迟消息
胡子哥5023 年前
Centos安装RabbitMQ,JavaSpring发送RabbitMQ延迟延时消息,JavaSpring消费RabbitMQ消息
java·centos·rabbitmq·延迟消息