如何构建飞书.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),保安查过记录后才能放行。
阅读全文