Webhooks和事件的平均水平
TL;DR
良好的webhook是签名(HMAC/mTLS),总结和偶发事件,是通过指数回程和收件人重复数据消除的at-least-once模型提供的。商定信封("event_id","type","ts","version","attempt","signature"),时间窗口(≤5分钟),响应代码,撤回,DLQ和状态终止。
1)角色和交付模式
发件人(您是/提供商):形成事件、签名、尝试传送至2xxx、在3xx/4xx/5xx时转发(显式的"不接受"除外)、运行DLQ,并给出replay API。
收件人(合作伙伴/您的服务):检查签名/临时窗口,进行去势和偶数处理,以正确的代码响应,提供/status 和/ack replay到"event_id"。
保修:at-least-once.收件人必须能够处理重复和顺序更改。
2)事件信封(envelope)
json
{
"event_id": "01HF7H9J9Q3E7DYT5Y6K3ZFD6M",
"type": "payout.processed",
"version": "2025-01-01",
"ts": "2025-11-03T12:34:56.789Z",
"attempt": 1,
"producer": "payments",
"tenant": "acme",
"data": {
"payout_id": "p_123",
"status": "processed",
"amount_minor": 10000,
"currency": "EUR"
}
}
必填字段:"event_id","type","version","ts","attempt"。
进化规则:添加字段;删除/更改类型-仅使用新的"版本"。
3)安全: 签名和绑定
3.1个HMAC签名(默认推荐)
标题:
X-Signature: v1=base64(hmac_sha256(<secret>, <canonical>))
X-Timestamp: 2025-11-03T12:34:56Z
X-Event-Id: 01HF7...
规范行:
<timestamp>\n<method>\n<path>\n<sha256(body)>
与收件人核对:
- abs(now − `X-Timestamp`) ≤ 300s
- "X-Ivent-Id"以前未处理(dedup)
- "X-Signature"匹配(与超时安全比较)
3.2 Dop。措施
IP/ASN allow-list.
mTLS用于高度敏感的webhook。
如果webhook触发回调,则用于sender-constrained的DPoP(可选)。
4)相似性和重复数据消除
4.1事件的幂等性
具有相同"event_id"的事件不应再次更改状态。收件人:- 将"event_id"存储在TTL ≥ 24-72小时的等效腰包(KV/Redis/DB)中;
- 保留处理结果(成功/错误,工件)以重新返回。
4.2指令的幂等(回调)
如果webhook导致客户端使用API(例如"确认付款"),请在REST调用卷上使用"Idempotency-Key",将结果存储在服务端(exactly-once outcome)。
KV模型(最低):
key: idempotency:event:01HF7...
val: { status: "ok", processed_at: "...", handler_version: "..." }
TTL: 3d
5)Retrai和backoff
推荐的图形(带有抖动的指数图):- 5 s、15 s、30 s、1 m、2 m、5 m、10 m、30 m、1 h、3 h、6 h、12 h、24 h"(更远的是每日到N天)
- 2 xx是成功的,停止回程。
- 4xx:
"400/ 401/403/404/422"-如果签名/格式为ok(客户端错误),则不重复。
"429"是"Retry-After"或backoff的复制品。
5 x/network-retraim。
发件人标题:"User-Agent","X-Webhook-Producer","X-Attempt"。
6)收件人一方的处理
伪工作表:pseudo verify_signature()
if abs(now - X-Timestamp) > 300s: return 401
if seen(event_id):
return 200 // идемпотентный ответ
begin transaction if seen(event_id): commit; return 200 handle(data) // доменная логика mark_seen(event_id) // запись в KV/DB commit return 200
事务性:"seen"标签必须原子化,并具有操作效果(或在提交结果后),以避免在发生故障时进行双重处理。
7)秩序保证和狙击
该命令没有得到保证。使用"ts"和"seq"/"version"域中的"data"进行相关性对账。
对于长滞后/损失-从发件人添加/replay 和/resync到收件人(通过时间/ID窗口获取快照和三角洲)。
8)状态、replay和DLQ
8.1发件人的Endpoint
"POST/webhooks/replay"-通过"event_id"列表或时间窗口。
"GET/webhooks/events/:id"-显示源软件包和尝试历史记录。
DLQ:"死"事件(retrais限制用尽)→单独的存储,alerta。
8.2收件人的终结
`GET /webhooks/status/:event_id` — `seen=true/false`, `processed_at`, `handler_version`.
"POST/webhooks/ack"-(可选)从DLQ确认手动处理。
9)错误合同(收件人的回应)
http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
Retry-After: 120
X-Trace-Id: 4e3f...
{
"error": "invalid_state",
"error_description": "payout not found",
"trace_id": "4e3f..."
}
建议:始终返回清晰的代码,如果可以,"Retry-After"。请勿返回详细的安全细节。
10)监控和SLO
度量(发送者):- delivery p50/p95, success rate, retrai/事件, drop-rate DLQ, share 2xx/4xx/5xx,延迟窗口高达2xx。
- verify fail rate(签名/时间),dup-rate, latency handler p95, 5xx。
- 交付:≥ 99。9%的事件获得2xx <3 c p95(在首次成功尝试之后)。
- 加密反驳:签名验证≤ 2-5 ms p95。
- Dedoop: 0重复效果(在域级别上只出现一个结果)。
11)数据安全和隐私
不要在webhook体内传播PAN/PII;通过授权的API在部件后面使用ID和后续拉动。
在日志中掩盖敏感字段;使用TTL将事件主体保持在最低限度。
加密DLQ存储和继电器。
12)转化与兼容性
"版本"(信封)和路径中的版本:"/webhooks/v1/payments"。
新字段-可选;删除-仅在"日落"期之后。
记录对machine-readable changelog(用于自动反驳)的更改。
13)测试桉例(UAT支票单)
- 重新交付相同的'event_id' →一个效果和'200'复制。
- 标题:正确的钥匙,不正确的钥匙,旧钥匙(轮换),"X-Timestamp"窗外。
- Backoff:收件人给出'429'的'Retry-After' →正确的暂停。
- 顺序:"……处理"事件早于"……created" →正确处理/等待。
- 收件人的DB在效果和"mark_seen"之间发生故障→原子/重复。
- DLQ和手动复制→成功交付。
- 大规模的"风暴"(包裹缝合提供者)→没有损失,限制不会扼杀关键。
14)迷你嗅探
发送者签名(伪签名):pseudo body = json(event)
canonical = ts + "\n" + "POST" + "\n" + path + "\n" + sha256(body)
sig = base64(hmac_sha256(secret, canonical))
headers = {"X-Timestamp": ts, "X-Event-Id": event.event_id, "X-Signature": "v1="+sig}
POST(url, body, headers)
收件人的检查和遗忘(伪的):
pseudo assert abs(now - X-Timestamp) <= 300 assert timingSafeEqual(hmac(secret, canonical), sig)
if kv.exists("idemp:"+event_id): return 200
begin tx if kv.exists("idemp:"+event_id): commit; return 200 handle(event.data) // доменная логика kv.set("idemp:"+event_id, "ok", ttl=259200)
commit return 200
15)经常出错
没有重复数据消除→重复效果(双重重复/重复)。
没有时间轴/窗口的签名→了replay漏洞。
为所有合作伙伴存储一个HMAC密码。
在确定结果之前的"200"答案→崩溃事件丢失。
将安全细节"冲洗"到响应/logi中。
缺少DLQ/反射-事件无法解决。
16)施帕加尔卡实施
Конверт: `event_id`, `type`, `version`, `ts`, `attempt`, `data`.
安全:HMAC v1+"X-Timestamp"+"X-Ivent-Id",窗口≤ 5分钟;根据需要,mTLS/IP allow-list。
交付:上空,背靠背,"Retry-After"、DLQ+replay API。
相似性:KV-kesh 24-72小时,效应原子固定+'mark_seen'。
可观察性:交付,签名,重复指标;"trace_id"跟踪。
文件:版本,响应代码,示例,UAT支票清单。
二.总结
持久的Webhooks建在三条鲸鱼上:签名信封,近距离交付和偶数处理。正式化合同,启用HMAC/mTLS和时间窗口,实现retrais+DLQ和恢复,存储等效标签,并捕获原子效应。然后,即使网络故障,负载峰值和罕见的"命运重复",事件仍保持可靠。