把「发送成功」当「送达」?我排查 IM 长消息被静默吞掉的完整过程

6 阅读2分钟

把「发送成功」当「送达」?我排查 IM 长消息被静默吞掉的完整过程

做无人值守内容矩阵时,我遇到一个非常隐蔽的坑:Agent 定时推送 IM 消息,日志里 rc=0 全是绿灯,可实际一条都没送达,服务端静默吞掉了。这篇文章记录完整排查过程,给同样跑自动化推送的人一个参考。

现象

某天早上看后台,日志一片绿:

2026-10-18 03:12:44 [INFO] push msg #1201 -> rc=0
2026-10-18 03:12:44 [INFO] push msg #1202 -> rc=0
2026-10-18 03:12:44 [INFO] push msg #1203 -> rc=0

rc=0 通常代表成功,但客户反馈一条都没收到。这是典型的「假成功」。

排查路径

1. 先排除网络层

检查了出网、连接池、重试次数,全部正常,排除网络抖动。

2. 对比「发送数」与「送达数」

这是关键一步。我把推送任务里记录的 sent_count 和 IM 服务端回执里的 delivered_count 拉出来对比:

# 伪代码示意
for task in tasks:
    sent = task.sent_count          # 我这边记的
    delivered = task.delivered_count  # 服务端回执
    if sent != delivered:
        logger.warning(f"task={task.id} sent={sent} delivered={delivered}")

一跑,发现 sent=3 delivered=0,问题锁定在服务端丢弃,而非网络。

3. 定位到「长消息被吞」

逐条对比后发现,被吞的全是超长消息(单条几万字)。IM 服务端对单条消息有长度上限,超限的直接静默丢弃,不返回错误码。

验证:把一条超长消息拆成 10 条短消息,全部送达。

解决方案

按长度拆包发送

MAX_LEN = 4000  # 服务端单条上限,按你对接的 IM 调整

def chunk_text(text: str, limit: int = MAX_LEN):
    return [text[i:i+limit] for i in range(0, len(text), limit)]

for chunk in chunk_text(long_text):
    push(chunk)

加「看门狗」对账

发完不是结束,起一个对账任务核对发送数与送达数,对不上就告警:

def watchdog():
    for task in tasks:
        if task.sent_count != task.delivered_count:
            alert(f"静默丢失: task={task.id}")

教训

  • rc=0 只代表「发出去了」,不代表「送达了」。自动化推送必须核对底层回执。
  • 长消息是静默丢弃的高发区,务必按长度拆包。
  • 无人值守系统里,「静默」比「报错」更可怕——报错能触发告警,静默只能靠对账兜底。

这套「拆包 + 对账看门狗」已经跑进我的矩阵,从「半夜静默」变成「半夜被吵醒但至少知道」。希望对你有用。