消息中间件进阶: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_BUSY、SERVICE_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_MESSAGE、PULL_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. ......
相关推荐
佛祖让我来巡山3 个月前
进阶应用篇:顺序消息、延迟消息、轨迹与过滤
延迟消息·顺序消息·轨迹与过滤
IT界的老黄牛4 个月前
RocketMQ 4.x 任意秒数延迟消息工程实战:MQ 粗延迟 + Redis 补精度 + MDC 链路透传
redis·rocketmq·事务消息·延迟消息
Java码农也是农2 年前
RabbitMQ使用延迟消息
java·rabbitmq·延迟消息
银氨溶液2 年前
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·延迟消息