Webhook 设计

服务端在事件发生时向订阅方回调 HTTP 请求的反向集成方式;要点是签名验证、重试策略、去重键与订阅生命周期管理,避免把可靠性问题转嫁给接收方。

Webhook 是什么

Webhook 是反向集成方式:服务方在事件发生时主动向订阅方预先登记的地址发起 HTTP 回调,订阅方收到后立即处理或入队。它把「轮询查询变化」变成「事件推送」,实时性与成本都更优,但可靠性责任被分散到两方:发送方要做签名、重试、退订与背压控制,接收方要做验签、去重与快速响应。双方对投递语义(通常是至少一次)达成一致,是这套机制能用的前提。

基本信息

方向服务方 → 订阅方的 HTTP POST 回调
投递语义通常为至少一次,接收方必须实现幂等
签名HMAC 签名 + 时间戳,接收方校验防伪造与重放
重试指数退避重试若干次,失败事件可查询或补发
订阅治理端点验证、停用、审计与配额,避免恶意地址被反复调用

典型使用场景

  • 支付结果通知:支付网关回调商户系统,商户据此更新订单状态
  • 代码与流水线事件:仓库推送、合并请求等事件触发自动化流程
  • 工单与审批状态:状态变化推送订阅方,减少轮询查询
  • 消息与通知:第三方平台推送用户消息或撤销事件
  • 数据同步:源系统变更后通知下游增量拉取或直接携带变更内容
  • 风控告警:规则命中时实时推送到安全平台

接收方校验要点(伪代码)

接收方校验要点(伪代码)
# 1 验签:用共享密钥对「时间戳 + 请求体」做 HMAC,比较结果
sign = HMAC_SHA256(secret, timestamp + '.' + raw_body)
if not verify(sign, header.signature): reject      # 防伪造
# 2 防重放:时间戳超出窗口直接拒绝
if now - timestamp > 300s: reject
# 3 幂等:按事件 ID 去重,已处理过直接返回成功
if event_id in processed_set: return 200
# 4 快速响应:校验通过后立即入队,重活异步处理,避免发送方超时重试
# 5 顺序:同一业务键的事件按时间戳排序处理,乱序时等待或丢弃后重拉

双方职责划分

事项发送方职责接收方职责
签名用共享密钥签名并带时间戳校验签名与时间窗口
重试指数退避重试并记录投递结果快速响应,不因处理慢而超时
去重保证事件 ID 稳定不变按事件 ID 幂等处理
失败可见提供投递日志与手动补发入口配置告警,处理失败时主动排查
停用多次失败后自动停用并通知修复端点后主动申请恢复
回调不可靠是常态:把「至少一次」写进对接文档并要求接收方幂等,比承诺「一定成功」更实际;发送方也应提供按时间范围补拉的兜底接口。
接收端点要在数百毫秒内返回:先落库或入队再处理,长耗时逻辑放到异步任务,否则发送方超时重试会让同一条事件多次到达。

易错点

  • 无签名校验:接收方被伪造请求诱导处理,产生错误业务状态
  • 回调无重试与投递日志:事件丢失后无法追溯与补发
  • 接收方同步做重活:超时触发重试,同一事件重复到达并重复处理
  • 无事件 ID 去重:重试与人工补发造成重复入账
  • 订阅端点无验证与管理:地址失效或伪造后长期无效投递,掩盖真实故障

数据来源与参考

  • Stripe 文档:Webhooks(签名与重试)
  • GitHub 文档:Webhooks
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌