如何构建飞书.NET SDK事件处理的去重与幂等性机制?
摘要:飞书事件处理过程中如何让你的应用不再"重复劳动",如何用三层防护筑起安全墙,结合内存与 Redis 双重保障,让你的飞书应用稳如磐石——不再重复处理,告别混乱状态。 为什么需要&a
飞书事件处理过程中如何让你的应用不再"重复劳动",如何用三层防护筑起安全墙,结合内存与 Redis 双重保障,让你的飞书应用稳如磐石——不再重复处理,告别混乱状态。
为什么需要"去重"?
想象一下这样的场景:
你在飞书里收到一条消息,应用收到通知后创建了待办事项。但因为网络不稳定,飞书以为你没收到,又发了一遍同样的通知——结果呢?你的应用又创建了一次待办,同一个任务出现了两次。
这就是我们所说的"重复处理"问题。
什么时候会出现这种情况?
在飞书事件驱动的世界里,以下情况都可能导致同一事件被多次送达:
📡 网络波动:飞书服务器没收到你的确认,于是重发
🔄 服务重启:内存清空,之前的事件又来了
👥 多实例运行:多个实例同时收到同一事件
🔌 断线重连:WebSocket 重连后可能重复消息
真实案例:一分钟内的混乱
时间线:
─────────────────────────────────────────────────────────────
09:00:00 飞书推送:收到一条新消息
09:00:01 实例A 接收并处理 → 创建待办 ✅
09:00:05 飞书没收到确认,再次推送
09:00:06 实例B 接收并处理 → 又创建待办 ❌
─────────────────────────────────────────────────────────────
后果有多严重?
问题
实际影响
📉 数据重复
用户收到两条相同的待办
💰 资金损失
订单重复扣款,退钱都退不完
📧 骚扰用户
同一条通知发十次
🔄 状态混乱
数据库里说"已处理",实际只做了一半
三层防护:像保安一样层层把关
Mud.Feishu SDK 设计了一套三层递进式防护机制,就像小区的三道岗哨,从外到内层层把关,确保不会有"坏人"(重复事件)混进来。
graph TB
subgraph AppLayer["应用层去重(业务键)"]
AppHandler["IdempotentFeishuEventHandler<T>"]
AppDesc["基于业务主键(消息ID、订单ID等)去重"]
end
subgraph DispatchLayer["分发层去重(EventId)"]
EventDedup["IFeishuEventDeduplicator / IFeishuEventDistributedDeduplicator"]
EventDesc["基于飞书事件ID去重,24小时窗口期"]
end
subgraph ProtocolLayer["协议层去重(SeqID/Nonce)"]
WS_Dedup["WebSocket: IFeishuSeqIDDeduplicator"]
Webhook_Dedup["Webhook: IFeishuNonceDistributedDeduplicator"]
ProtoDesc["基于消息序列号去重,过滤重复消息"]
end
AppHandler -->|处理器内部业务逻辑| EventDedup
EventDedup -->|事件路由与分发| WS_Dedup
EventDedup -->|事件路由与分发| Webhook_Dedup
style AppLayer fill:#e1f5ff
style DispatchLayer fill:#fff4e1
style ProtocolLayer fill:#ffe1e1
这三层分别负责什么?
可以把这三层想象成工厂流水线上的三个质检员:
质检员
检查什么?
在哪检查?
过滤范围
适合什么时候用?
协议层
消息编号(SeqID)
消息刚到达时
每条消息
过滤网络重复,最外层防护
分发层
事件ID(EventId)
事件分发前
每个事件
过滤飞书重发,中间层防护
应用层
业务键(TaskId)
业务处理时
每次业务操作
防止逻辑重复,最后一道防线
分发层:事件的"身份证检查"
这是最核心的一层,就像门口的保安,每个进来的事件都要先出示身份证(EventId),保安查过记录后才能放行。
