揭秘Pulsar消息定位:如何精准消费积压数据

🌊 消息海洋中的导航坐标

Apache Pulsar的消息系统如同一条永不停息的数据河流,每条消息都是这条河流中的独特水滴。消费者需要精确的定位系统来捕捉那些未被处理的消息"水滴"。

📍 Pulsar中的坐标系统

每条消息都有专属的三维坐标,通过以下方式精确定位:

ledgerId:entryId:partitionIdx

🔍 含义详解
🧱 1. ledgerId
这是指当前消息所在的 BookKeeper ledger 文件的 ID。

  • Pulsar 使用 BookKeeper 存储消息。
  • 每个 ledger 是 BookKeeper 写入的一个文件(或逻辑段)。
  • ledgerId 是一个全局唯一的长整型编号,用来标识这个 ledger。

📦 一个 topic 通常由多个 ledger 组成,随着时间滚动、数据分片等原因会不断创建新 ledger。

📄 2. entryId
这是该 ledger 中的具体消息(entry)编号,表示第几条消息。

  • 从 0 开始递增。
  • 每条消息写入时都会获得一个顺序的 entryId。

坐标解析示例

以387529368:2727:-1为例:

  • 387529368 (ledgerId):消息存储的物理账簿编号
  • 2727 (entryId):账簿内的消息序列号(从0开始)
  • -1 (partitionIdx):非分区主题标识(分区主题显示如0)

🎯 两个关键位置标记

🧭 markDeletePosition 的语义:它表示该订阅已经成功 ack 的最后一条消息的“前一条”位置

{'markDeletePosition': '390694044:218'}

也就是说:

  • 订阅实际已确认的最后一条消息是:387529368:2727
  • 下一条需要消费的位置是:387529368:2728

Pulsar 是这样处理的:

  • markDeletePosition 是“已删除(已确认)的位置”
  • 所以实际消费进度是从下一个位置开始的
  1. readPosition
{'readPosition': '390694044:246'}

📌 两者的区别

指标名含义
markDeletePosition已 ack 的最后一条消息的位置
readPosition下一条将被推送给消费者的位置,即当前消费进度

✅ 示例图示

LedgerID:EntryID
    |
    v
387529368:2725  ✅ acked  
387529368:2726  ✅ acked  
387529368:2727  ✅ acked   <-- markDeletePosition(最后一条已确认消息)

387529368:2728  📤 已发送,未 ack   <-- 已推送但未确认(in-flight 消息)
387529368:2729  ⏳ 待发送          <-- readPosition(下一条将被推送的消息)

说明:
- `markDeletePosition` 表示消费者已确认(ack)消息的最后位置。
- `readPosition` 表示下一条将被推送给消费者的消息位置。
- `markDeletePosition``readPosition` 之间的消息可能已被发送,但尚未被 ack,处于“in-flight”状态。

🚀 积压数据处理策略

当需要重新处理消息时,选择正确的起始点至关重要:

场景起始位置代码实现
处理所有未确认消息,无论它们是否已被客户端读取过markDeletePosition+1entry_id += 1
处理全新未读消息readPosition+1不推荐常规使用

正确代码实现:

# ---------- 辅助:计算 markDeletePosition 的下一条 ----------
def next_message_id(pos: str) -> [int, int]:
    """返回 (ledgerId, entryId) -> markDeletePosition 的下一条"""
    led, ent = map(int, pos.split(":"))
    if led == -1 and ent == -1:  # 从未确认过 -> 从 Earliest 开始
        return 0, 0
    ent = ent + 1
    return led, ent
    
# 从管理接口获取标记位置
led, ent = next_message_id(pos['markDeletePosition'])
# 构建消息ID
msg_id = pulsar.MessageId(ledger_id=led, 
                          entry_id=ent,
                          partition_idx=part_idx)

💥 常见陷阱与解决方案

陷阱1:参数顺序错位

# 危险写法(易错参数顺序)
msg_id = pulsar.MessageId(ent, led, part_idx)  # 反序参数!

# 安全写法(明确参数命名)
msg_id = pulsar.MessageId(
    ledger_id=led,
    entry_id=ent,
    partition_idx=part_idx
)

陷阱2:混淆位置指针

{
  "markDeletePosition": "390647951:17508",  // 已确认位置
  "readPosition": "390647951:17530"         // 已读取位置
}
  • ✅ 正确起始点:17509(未确认消息起点)

  • ❌ 错误起始点:17531(跳过未确认消息)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值