Platform

什么是幂等性?如何处理重复的 webhook?

幂等性使重复操作产生与单次操作相同的效果,因此处理重复 webhook 时应避免重复执行其工作。

连接中断可能让你无法确定发送请求是否成功。丢失的确认也可能导致 webhook 发送方投递一个你的应用已经存储过的事件。

这两种故障发生在相反的方向。Bird 可以通过你提供的键识别重复的 API 请求。你的 webhook 接收端需要自行记录已接受的事件。

如何安全地重试发送?

对同一逻辑 API 操作的每次尝试,复用相同的 Idempotency-Key 请求头。

你自行选择键,最多 255 个字符,并在重试时保持不变。使用稳定的值(如 welcome-user/usr_abc123)可以在进程重启后仍能标识同一条欢迎消息操作。

该请求头适用于变更类请求,如 POSTPATCHDELETE。不带键的请求会跳过此去重机制。GET 忽略该请求头,因为读取资源本身就可安全重复。

Bird 会为匹配的已完成请求返回存储的响应,包括其原始状态码和响应体。响应中携带 Idempotency-Replay: true,方便你在日志中识别该复用。

幂等性指南记录了已完成响应窗口的默认时长为三小时。窗口过期后的重试会作为新操作执行。不要依赖该键来永久防止重复发送。

Bird SDK 会为变更操作生成键,并在内部重试中复用。Bird CLI 也会在缺少键时为变更请求生成键。当多次独立的命令调用需要共享同一操作时,请显式设置 --idempotency-key

对于 SMTP 提交,使用 X-Bird-Idempotency-Key 消息请求头。这样重试的提交可以标识为同一操作。

如果错误地复用了键会怎样?

Bird 会拒绝冲突的键使用,而不会返回另一个操作的响应。

场景响应与恢复方式
完成后使用相同的键和请求返回存储的响应,附带 Idempotency-Replay: true
已完成的键被用于不同的请求409,附带 E01005,表示幂等键被复用。修正键后再重试。
使用该键的另一个请求仍在运行409,附带 E01004,表示请求正在处理中。稍等片刻后重试。
键超过 255 个字符400,附带 E01002,表示输入无效。缩短键的长度。

比较范围包括方法、端点、路径参数、查询字符串和请求体。对于 JSON,更改空白字符会改变请求的标识,因此在重试时应保留原始请求体。

未完成操作的锁在三十秒内过期。该限制允许另一个请求在被放弃的操作之后继续执行,但并不能确定副作用是否已经发生。

Bird 不会存储 5xx 响应以供重放。使用相同的键重试服务器错误或超时,以便仍可复用已记录的成功响应。

验证错误或业务规则拒绝会释放该键。你可以修正被拒绝的请求并使用同一键重试,因为没有保留已完成的响应。

为什么我会收到相同的 webhook 两次?

如果 Bird 未收到成功响应,它可能会重试一个你的接收端已存储的事件。

接收端可能在连接断开前刚好存储了事件。Bird 没有看到成功的确认,因此会重试,即使接收端已经拥有该事件。

每次重试都保留相同的 webhook-id 请求头,用于标识该事件。错过投递的重放也保留该标识符,因此两者都可被识别为同一事件。

如何使我的处理程序具有幂等性?

在调度事件的工作之前,使用数据库唯一约束存储每个 webhook-id

先查询是否存在再插入会留下竞态条件:两个并发请求都可能看到没有记录。让数据库来拒绝重复的标识符。

在同一事务中存储标识符和任务。这可以防止标识符被记录而没有任务被入队。

  1. 验证请求,然后在同一事务中插入其标识符和任务。
  2. 在该事务提交后返回 2xx,这样 Bird 就可以停止重试。
  3. 在一个能够安全重复自身操作的 worker 中处理已存储的任务。

对于已提交的重复标识符,返回成功而不创建另一个任务。对于失败的事务,返回错误以便 Bird 重试。

将耗时的工作移出接收端,因为等待它可能导致请求超时。Worker 可能因与 webhook 投递无关的原因而重试,因此仅保护接收端是不够的。

事件也可能乱序到达。在覆盖较新状态之前,比较 timestamp 中的事件时间。失败的 webhook 重试包含部分费用示例。

不应依赖什么?

不要假设请求去重能使重复的副作用完全不可能发生。

如果 Bird 的去重存储不可用,请求会照常处理。在重复操作可能造成损害的地方,保留业务层面的安全措施。

同样,webhook-id 区分的是同一事件的重复投递。不同的事件有不同的标识符。你的应用仍需自行判断这些事件是否应当重复执行相同的操作。

幂等性记录了 API 的重试行为。Webhooks 涵盖了你的接收端需要处理的独立投递保证。

简而言之

  1. API 重试和 webhook 重试需要不同的记录。

    向 Bird 发送请求时复用 Idempotency-Key。你的接收端存储 webhook-id 来识别已接受过的事件。

  2. 被拒绝的请求可以释放其键。

    验证错误和业务规则错误不会留下已完成的响应,允许在同一键下修正后重试。

  3. 已完成的键不能标识不同的请求。

    更改 JSON 的请求体或端点可能产生 409 冲突。应修正键,而非原样重试该冲突。

  4. 去重有其局限性。

    如果去重存储不可用,请求会照常处理。在你的应用中也应确保重复操作不会造成损害。

基于同一网络构建。

测试 API 密钥即刻获取。添加付款方式并验证发送者身份后,即可解锁生产环境。

你的下一个创意。
随时连接。