Pulsar 延迟消息(Delayed Message) 是一个非常实用的功能,允许你发送一条消息后,在未来的某个时间点才被消费者接收到。这在定时任务、订单超时处理、提醒系统、重试机制等场景中极为重要。
Pulsar 通过 deliverAt 和 deliverAfter 两个 API 支持延迟消息,底层基于 Broker 的 延迟队列机制 实现,性能高、精度好。
📘 Pulsar 延迟消息(deliverAt / deliverAfter)详解
一、什么是延迟消息?
- 延迟消息:生产者发送消息时指定一个“投递时间”,Broker 会将该消息暂存,直到时间到达后才推送给消费者。
- 消费者无法感知消息是“实时”还是“延迟”发送的,逻辑透明。
- 适用于:
- 订单 30 分钟未支付自动关闭
- 用户注册后 24 小时发送提醒
- 重试任务延迟执行
- 定时通知、定时对账
✅ Pulsar 的延迟消息是 精确投递,不是“大约时间”。
二、两种延迟方式
Pulsar 提供两种设置延迟的方式:
| 方法 | 说明 | 示例 |
|---|---|---|
deliverAt(long timestamp) | 指定具体时间戳(毫秒)投递 | System.currentTimeMillis() + 60_000 |
deliverAfter(long delay, TimeUnit) | 指定延迟多久后投递 | 10, TimeUnit.MINUTES |
三、使用示例(Java)
1. deliverAfter:延迟一段时间后投递
Producer<String> producer = client.newProducer(Schema.STRING)
.topic("orders")
.create();
// 发送一条消息,10分钟后才可被消费
producer.newMessage()
.value("Order #12345: Close if not paid")
.deliverAfter(10, TimeUnit.MINUTES)
.send();
System.out.println("Message scheduled for delivery in 10 minutes");
2. deliverAt:指定具体时间投递
long deliverTime = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); // 1小时后
producer.newMessage()
.value("Reminder: Meeting starts soon")
.deliverAt(deliverTime)
.send();
3. 消费者正常消费(无需特殊处理)
Consumer<String> consumer = client.newConsumer(Schema.STRING)
.topic("orders")
.subscriptionName("order-closer")
.subscribe();
// 消费者只会收到“已到期”的消息
Message<String> msg = consumer.receive();
System.out.println("Received: " + msg.getValue());
consumer.acknowledge(msg);
✅ 消费者无感知,延迟由 Broker 透明处理。
四、延迟消息的实现原理
Pulsar 并没有使用外部调度系统(如 Quartz),而是基于 Broker 内置的延迟队列机制 实现。
1. 核心组件:Delayed Delivery Tracker
- Broker 维护一个基于时间轮(Timing Wheel)或优先级队列的延迟调度器。
- 延迟消息不会立即进入 Topic 的主日志,而是先写入一个 延迟索引。
- 到达指定时间后,Broker 将消息“释放”到主队列,供消费者拉取。
2. 存储位置
- 延迟消息仍然写入 BookKeeper 持久化存储。
- 只是消费指针(Cursor)被延迟推进。
3. 流程图(文字描述)
Producer → Broker
↓
消息标记为“延迟”
↓
写入 BookKeeper + 延迟索引(按时间排序)
↓
[等待到期]
↓
延迟到期 → 消息从延迟队列释放到主队列
↓
Consumer 正常拉取(如同普通消息)
✅ 高可用:即使 Broker 重启,延迟消息也不会丢失。
五、延迟消息的特性与限制
| 特性 | 说明 |
|---|---|
| ✅ 持久化 | 消息写入 BookKeeper,不丢失 |
| ✅ 高精度 | 延迟精度可达毫秒级 |
| ✅ 支持所有订阅模式 | Exclusive、Shared、Key_Shared 等均支持 |
| ✅ 支持批量消息 | 批量中的每条消息可独立设置延迟 |
| ✅ 与 Schema 兼容 | 支持 JSON、Avro 等 Schema |
| ⚠️ 最大延迟时间 | 默认 1 天,可通过配置调整 |
| ⚠️ 不支持非持久化 Topic | non-persistent:// 不支持延迟消息 |
六、配置延迟消息参数
1. 设置最大延迟时间(Broker 配置)
在 broker.conf 中设置:
# 最大延迟时间(默认 1 天)
managedLedgerMaxUnackedRangesForCursors=10000
# 延迟队列最大条目数(可选)
# 默认已启用
⚠️ Pulsar 2.10+ 版本默认启用延迟消息功能。
2. 调整延迟精度(可选)
# 延迟检查间隔(默认 1ms,不建议修改)
brokerDelayedDeliveryTriggeringTickTimeMillis=1
七、延迟消息 vs 其他方案对比
| 方案 | 延迟消息 | 定时任务(Cron) | 外部调度(Quartz) | NACK + 重试 |
|---|---|---|---|---|
| 精度 | ✅ 高(毫秒) | ⚠️ 分钟级 | ⚠️ 秒级 | ⚠️ 不精确 |
| 持久化 | ✅ 是 | ✅ 是 | ✅ 是 | ✅ 是 |
| 实现复杂度 | ✅ 简单 | ⚠️ 需维护任务 | ❌ 复杂 | ⚠️ 需幂等 |
| 与消息系统集成 | ✅ 原生支持 | ❌ 独立系统 | ❌ 需集成 | ✅ 支持 |
| 适用场景 | 定时投递 | 周期任务 | 复杂调度 | 重试 |
✅ 延迟消息是“一次性定时投递”的最佳选择。
八、典型应用场景
1. 订单超时关闭
producer.newMessage()
.value("Close order: " + orderId)
.deliverAfter(30, TimeUnit.MINUTES)
.send();
2. 用户行为提醒
// 注册后 24 小时提醒
producer.newMessage()
.value("Welcome back, complete your profile!")
.deliverAfter(24, TimeUnit.HOURS)
.send();
3. 重试机制(替代固定 NACK)
// 第一次失败后,5分钟后重试
producer.newMessage()
.value(originalPayload)
.property("retry-count", "1")
.deliverAfter(5, TimeUnit.MINUTES)
.send();
✅ 比“立即 NACK”更温和,避免消息风暴。
4. 定时对账 / 报表生成
// 每天凌晨 2 点触发
long midnight = LocalDateTime.now().plusDays(1).withHour(2).withMinute(0).withSecond(0).toInstant(ZoneOffset.UTC).toEpochMilli();
producer.newMessage()
.value("Generate daily report")
.deliverAt(midnight)
.send();
九、最佳实践建议
| 实践 | 建议 |
|---|---|
✅ 使用 deliverAfter 优先 | 更直观,避免时区问题 |
| ✅ 避免过长延迟 | 建议不超过 7 天(影响性能) |
| ✅ 结合 Schema 使用 | 保证数据结构一致 |
| ✅ 监控延迟消息积压 | 防止 Broker 内存压力过大 |
| ✅ 业务幂等性设计 | 防止重复处理(如订单关闭两次) |
| ✅ 不要用延迟消息替代 Cron | 周期任务仍用调度系统 |
| ✅ 日志记录投递时间 | 便于调试 |
十、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 消息未延迟 | 未调用 deliverAt/deliverAfter | 检查代码 |
| 延迟不生效 | Topic 为 non-persistent | 改用 persistent:// |
| 延迟过长导致失败 | 超出 maxDelay 限制 | 调整 Broker 配置 |
| 消费者提前收到 | 时钟不同步 | 确保集群时间同步(NTP) |
| 性能下降 | 大量延迟消息 | 分 Topic 或使用外部调度 |
✅ 总结
| 特性 | 说明 |
|---|---|
| ✅ 原生支持 | Pulsar 内置,无需外部依赖 |
| ✅ 高精度 | 毫秒级延迟,准时投递 |
| ✅ 持久化 | 消息不丢失,Broker 重启仍有效 |
| ✅ 简单易用 | 一行代码设置延迟 |
| ✅ 与现有消费模型无缝集成 | 消费者无感知 |
| ✅ 适用广泛 | 超时、提醒、重试、定时任务 |
📌 一句话总结:
Pulsar 延迟消息是“定时投递”的银弹 —— 通过
deliverAt和deliverAfter,你可以轻松实现高可靠、高精度的延迟任务,无需引入复杂调度系统。
1062

被折叠的 条评论
为什么被折叠?



