消息自动化不是写几个回调处理器就完事。消息量大了之后,超时、重复、乱序、积压这些问题会一个个冒出来。我们从一个简单的回调服务演进到能扛日均十万条消息的处理系统,中间加了队列、状态机、幂等、降级四层保障。这篇讲系统架构和每层解决的问题。
底层用的是Eyun 平台开放的企微 API,凭证在Eyun 平台首页开通,Webhook 收消息,message 模块发消息。
系统架构:四层拆解
系统分四层:接入层、队列层、处理层、发送层。
接入层只做一件事:收 Webhook 回调,验签,去重,落库,推队列。5 秒内返回 200,不做任何业务处理。
队列层用 Redis List,按会话(uin 或 roomId)分队列。同一会话的消息串行处理,不同会话并行,既保序又保吞吐。
处理层从队列取消息,做意图识别、业务调用、生成回复。处理结果发回发送层。
发送层调 message/sendText 或 sendRichText 发消息,发完存流水。
幂等:delivery_id 去重
回调至少一次投递,同一条消息可能推多次。用X-Eyun-Delivery请求头做去重键,Redis SET NX 判断,24 小时内只处理一次。
delivery_id = request.headers.get("X-Eyun-Delivery") if not redis.setnx(f"msg:{delivery_id}", 1): return "ok" # 重复投递,直接跳过 redis.expire(f"msg:{delivery_id}", 86400)去重放在接入层,处理层不用关心重复问题。消息体字段在开发文档里有完整说明。
保序:按会话串行
客户连发三条消息,如果并发处理可能乱序回复。按会话分队列,同一 uin 的消息进同一个队列,消费线程串行处理。
实现上用 Redis 的lpush入队、brpop阻塞出队。多个消费者轮询多个队列,同一队列只有一个消费者在处理。
降级:处理超时回兜底
处理层调业务接口或模型可能超时。单条消息处理超过 3 秒,走降级逻辑——回"正在处理,请稍候",后台异步重试,结果出来后再补发。
降级保证用户不会等太久,也不会因为一次超时导致整条链路阻塞。
监控:四个核心指标
系统稳定不稳看四个数:
回调接收量:每分钟收到多少条,突然下降说明回调链路有问题
队列积压量:积压超过阈值告警,说明处理速度跟不上
处理成功率:低于 99% 告警
端到端延迟:从收到到回复超过 5 秒告警
这四个数稳定,系统就基本稳定。接口字段在开发文档里有完整说明。
架构的价值在可扩展
四层拆开后,每层可以独立扩展。消息量大了加处理消费者,发送慢了加发送线程,队列满了换 Kafka。系统从日均几千条到十万条,只加了机器,没改架构。