Serverless Web3 Webhook:链上事件回调要能去重和补偿
Serverless Web3 Webhook链上事件回调要能去重和补偿DApp 后端经常用 Serverless 接收链上事件 webhook比如交易确认、NFT 转移、合约事件。Serverless 很方便但链上事件回调可能重复、乱序、延迟甚至漏投。函数能触发不代表事件处理可靠。Web3 webhook 的核心是去重、顺序和补偿不是写一个 handler。在实际工程中Web3 webhook 的最大挑战是链上事件的确定性和webhook 交付的不确定性之间的矛盾。链上事件本身是确定的一旦确认就不会变但 webhook 交付是不确定的可能重复、可能丢失、可能延迟。如果系统假设webhook 一定会送达且只送达一次就会在数据一致性上出问题。工程上更稳健的做法是把 webhook 当成事件通知而不是事件本身。收到 webhook 后先去链上确认事件是否真的存在、是否已经确认足够区块再决定是否处理。这样即使 webhook 重复或丢失也不会影响数据正确性。更深层的问题是Serverless 的无状态特性和链上事件的有状态特性之间的冲突。链上事件是连续的按区块高度、交易索引、log 索引排序但 Serverless 函数是无状态的每次执行都是独立的。如果多个函数同时处理不同事件可能会因为竞态条件导致状态不一致如两个函数同时更新同一个用户的余额。生产级系统需要设计事件排序或状态锁要么按事件顺序串行处理如用队列要么在更新状态时加乐观锁版本号或 CAS。无状态函数处理有状态事件必须在架构层面解决一致性问题。一、事件处理链路flowchart TD A[Chain Event] -- B[Webhook Provider] B -- C[Serverless Function] C -- D[Idempotency Check] D -- E[Event Store] E -- F[Business Handler]先落事件再处理业务。不要在函数里直接改业务状态后就结束。二、事件 ID 要唯一链上事件可以用 chainId、txHash、logIndex 组合成唯一键。const eventId ${chainId}:${txHash}:${logIndex};写入事件表时用唯一索引重复回调直接跳过。三、处理要幂等CREATE TABLE chain_events ( event_id TEXT PRIMARY KEY, status TEXT NOT NULL, payload JSONB, created_at TIMESTAMP );业务处理失败时把状态标记为 failed后续由补偿任务重试。不要只靠 webhook provider 重试。四、漏事件要能补扫定时任务按区块高度补扫和 webhook 数据对账。reconcile: from_block: last_confirmed_block to_block: current_block - confirmations compare_event_store: true链上事件系统没有补偿扫描就不适合承载关键业务。在生产环境中补扫区块的一个常见踩坑是补扫和 webhook 的重复处理。比如补扫任务正在扫描区块 100-200同时 webhook 也在推送区块 150 的事件就可能同一个事件被处理两次。工程上需要做幂等性保护事件 ID 唯一索引、处理状态检查、或者给补扫任务加锁如 Redis 分布式锁确保同一时间只有一个任务在处理某个区块范围。另一个边界场景是链重组导致的事件失效。以太坊等区块链可能发生链重组reorg导致之前确认的事件变成未确认甚至被替换。如果系统已经处理了某个事件但后来链重组了这个事件可能就从链上消失了。生产级系统需要设计链重组检测和处理定期对比本地事件表和链上事件如果发现本地有但链上没有的事件就标记为已回滚并回滚对应的业务状态。这种处理在技术上不复杂用区块哈希对比即可但很多团队在早期设计时忽略等到真的遇到重组时才发现问题。还要考虑区块确认数。刚监听到事件不代表最终确定链重组可能让事件失效。关键业务通常要等若干确认后再进入最终状态。confirmation_policy: ethereum_mainnet: 12 testnet: 3 high_value_transfer: 24Serverless 函数可以先记录 pending再由确认任务把事件推进 confirmed。这样状态变化更符合链上现实。如果 webhook provider 延迟或失败补扫任务必须能从上次确认区块继续。不要把可靠性完全寄托在第三方回调。五、总结Serverless 处理 Web3 webhook 时要用 chainId、txHash、logIndex 去重先落事件再幂等处理并通过区块补扫做补偿。函数触发只是开始。事件可靠DApp 状态才可靠。Web3 后端最怕“偶尔漏一条”。链上事件少一条用户资产状态就可能错一片。去重、确认、补偿缺一不可。Serverless 还要注意执行时间限制。补扫区块、批量处理事件、拉取链上详情都可能超过函数超时。长任务应该拆成队列任务函数只负责接收和入队。serverless_event_split: webhook_function: validate_and_enqueue worker: process_event reconciler: scan_missing_blocks这样即使某次处理变慢也不会让 webhook provider 认为接口不可用。事件 payload 也要原样保存一份。业务字段解析错了还可以从原始 payload 重新回放。没有原始事件补偿会少一条退路。