Webhook交付保證
Webhooks是通過HTTP(S)從系統到訂戶的異步通知。網絡不可靠:答案丟失,數據包重復或脫序。因此,交付保證不是「通過TCP」構建的,而是基於webhook協議和域冪等級的。
關鍵目標:提供按鍵順序(在需要時)的在線交付,為訂戶提供用於等效處理的材料和用於還原的回收工具。
1)保障水平
Best-effort是一次嘗試,沒有後退。僅適用於「不重要」事件。
One-least-once(推薦)-可以重復和退出訂單,但事件將在合理的時間內交付,但前提是訂戶可用。
Effectively-exactly-once(在效果級別)-通過在訂戶/發件人側面的冪等和減速存儲相結合來實現。在HTTP傳輸上,「exactly-once」是不可能的。
2)webhook合同: 最低要求
標題(示例):
X-Webhook-Id: 5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21 # глобальный ID события
X-Delivery-Attempt: 3 # номер попытки
X-Event-Type: payment.authorized.v1 # тип/версия
X-Event-Time: 2025-10-31T12:34:56Z # ISO8601
X-Partition-Key: psp_tx_987654 # ключ порядка
X-Seq: 418 # монотонный номер по ключу
X-Signature-Alg: HMAC-SHA256
X-Signature: t=1730378096,v1=hex(hmac(secret, t body))
Content-Type: application/json
身體(示例):
json
{
"id": "5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21",
"type": "payment.authorized.v1",
"occurred_at": "2025-10-31T12:34:56Z",
"partition_key": "psp_tx_987654",
"sequence": 418,
"data": {
"payment_id": "psp_tx_987654",
"amount": "10.00",
"currency": "EUR",
"status": "AUTHORIZED"
},
"schema_version": 1
}
收件人要求:在簽名緩沖和驗證後快速回答「2xx」並異步進行業務處理。
3)順序和因果關系
按鍵排序:保修僅在單個「partition_key」(例如,「player_id」,「wallet_id」,「psp_tx_id」)內部「不會離開」。
全球秩序沒有得到保障。
發件人側面是按鍵序列化的隊列(單個消費者/sharding),收件人側面是inbox,帶有「(源,event_id)」,可選地等待錯過的「seq」。
如果跳過是關鍵的-提供pull-API "GET/events?after=checkpoint的「追趕和鉆探」狀態。
4)相似性和重復數據消除
每個Webhook都帶有穩定的「X-Webhook-Id」。
收件人存儲「inbox(event_id)」:PK-'source+event_id';重播→無操作。
當事件第一次出現「視圖」時,副作用(寫入DB/錢包)僅執行一次。
對於「有效果」命令,請使用Idempotency-Key和Retrace窗口期間的結果緩存。
5)Retrai,backoff和窗戶
Retraes政治(參考):- 在「5xx/timeout/connection error/409-Conflict (retryable)/429」上轉播。
- 除了「409/423/429」(並且僅在一致的語義下)之外,不要重述「4xx」。
- 指數backoff+ full jitter: 0。5s, 1s, 2s, 4s, 8s, …最多'max=10-15 min';中繼窗口的TTL:例如72小時。
- 尊重收件人的「Retry-After」。
- 有一個共同的截止日期:「承認事件未交付」並將其轉換為DLQ。
yaml retry:
initial_ms: 500 multiplier: 2.0 jitter: full max_delay_ms: 900000 ttl: 72h retry_on: [TIMEOUT, 5xx, 429]
6) DLQ и redrive
DLQ是有毒或過期的TTL事件的「墓地」,具有完整的元信息(標題,標題,錯誤,嘗試,散列)。
基於Web 的控制臺/API for redrive(點對點重新交付),可選用endpoint/secret編輯。
具有優先級的Rate-tended redrive和batch-redrive。
7)安全性
mTLS(如果可能)或TLS 1。2+.
身體簽名(帶有per tenant/endpoint秘密的HMAC)。驗證:1.從標題中檢索「t」 (timestamp),檢查滑動窗口(例如,± 5分鐘)。
8)配額,比例限制和公平
Fair-Queue per tenant/subscriper: 確保一個訂戶/tenant不會得分。
出站流量和per-endpoint的配額和爆破限制。
對「429」的反應:尊重「Retry-After」,旋轉;長時間限制為degrade(僅發送關鍵事件類型)。
9)訂閱生命周期
Secret rotation: `current_secret`, `next_secret` с `switch_at`.
註冊/驗證:POST endpoint →挑戰/響應或樂隊外確認。
Lease(根據需要):簽名有效期為「valid_to」;延長是顯而易見的。
Test ping:在打開主拓撲之前檢查路由的人工事件。
健康樣本:定期進行HEAD/GET,並進行簡介檢查和TLS。
10)模式的演變(事件版本)
事件類型的轉換: '付款。authorized.v1` → `…v2`.
進化-進化(新字段→ API的MINOR版本),破解→新類型。
電路寄存器(JSON-Schema/Avro/Protobuf)+在發送前自動驗證。
標題「X-Ivent-Type」和主體中的「schema_version」字段都是必需的。
11)可觀察性和SLO
度量(按類型/tenant/訂閱者):- `deliveries_total`, `2xx/4xx/5xx_rate`, `timeout_rate`, `signature_fail_rate`.
- 「attempts_avg」,「p50/p95/p99_delivery_latency_ms」(從發布到2xx)。
- `dedup_rate`, `out_of_order_rate`, `dlq_rate`, `redrive_success_rate`.
- `queue_depth`, `oldest_in_queue_ms`, `throttle_events`.
- 交貨比例≤ 60 c(p95)-99。5%用於關鍵事件。
- DLQ ≤ 0.24小時1%
- Signature failures ≤ 0.05%.
Логи/трейсинг: `event_id`, `partition_key`, `seq`, `attempt`, `endpoint`, `tenant_id`, `schema_version`, `trace_id`.
12)發件人參考算法
1.將事件寫入事務性outbox。
2.定義partition_key和seq;放在隊列中。
3.Vorker按鍵,生成查詢,簽名,隨時間表(connect/read)發送。
4.在「2xx」-承認交付,固定潛伏期和seq-checkpoint。
5.根據「429/5xx/timeout」,根據該政策。
6.通過TTL → DLQ和alert。
13)參考處理程序(收件人)
1.接受請求,檢查TLS/proto。
2.簽名驗證和時間窗口。
3.快速ACK 2xx(同步寫入本地inbox/隊列後)。
4.異步修補程序讀取「inbox」,檢查「event_id」(擦除),如果需要-在「partition_key」內通過「seq」排序。
5.為reconcile編寫「offset/seq checkpoint」來執行效果。
6.如果出現錯誤,則為本地轉發;「有毒」任務→帶有警報的本地DLQ。
14) Reconcile(子彈回路)
對於「不可阻擋」事件:- `GET /events?partition_key=...&after_seq=...&limit=...'-放棄所有錯過的。
- 令牌支票:'after=opaque_token'而不是seq。
- 相同的「event_id」,新的「t」上的相同簽名。
15)有用的標題和代碼
2 xx-接受(即使以後進行業務處理)。
410 Gone-endpoint已關閉(發送者停止交付並將訂閱標記為「存檔」)。
409/423-暫時阻止資源→ retray是明智的。
429-常常→顫抖和後退。
400/401/403/404-配置錯誤;停止retrai,打開tiket。
16)多重特南特和地區
單獨的per tenant/endpoint隊列和限制。
數據駐留:從區域發送數據;「X-Tenant」,「X-Region」的端到端標題。
故障隔離:一個訂戶的下降不會影響其他訂戶(separate pools)。
17)測試
合同測試:固定的實體/簽名示例,驗證驗證。
混亂:丟棄/復制,交換順序,網絡延遲,「RST」,「TLS」錯誤。
載荷:暴風雨,p95/p99測量。
安全:反倒帶、過時的時間戳、錯誤的秘密、輪換。
DR/Replay:在孤立的展位中從DLQ中大量重播。
18)花花公子(runbooks)
1.「signature_fail_rate」的增長'
檢查「tolerance」過時的時鐘漂移,秘密的輪換;暫時啟用「dual secret」。
2.隊列正在老化('oldest_in_queue_ms' ↑)
增加竊聽者,包括關鍵斧頭的優先級,暫時降低「嘈雜」類型的頻率。
3.訂閱者的風暴「429」
在嘗試之間包括trottling和暫停;移動不太關鍵的事件類型。
4.大眾的「5xx」
打開特定端點的circuit breaker,轉換為defer&batch模式;向訂戶發出信號。
5.DLQ填充
停止非關鍵發布,啟用低RPS的擊球重播,提高訂閱所有者的差異。
19)典型錯誤
同步重處理,直到2xx回應→ retrai和重復。
沒有主體/時間窗口簽名,→存在備用/反射漏洞。
缺乏「event_id」和「inbox」 →是無法實現的。
嘗試「全局秩序」→永久隊列鎖定。
沒有刺傷/限制的獵犬→事件增加(thundering herd)。
所有訂戶的單一共享池→「嘈雜」地放置所有人。
20)售前支票清單
- 合同:'event_id'、'partition_key'、'seq'、'event_type。vN', HMAC簽名和timestamp。
- 發件人:outbox,按鍵序列化,帶有backoff+jitter,TTL,DLQ和redrive的轉發。
- 收件人:快速寫入inbox+2xx;等效處理;本地DLQ。
- 安全性:TLS,簽名,反倒帶,雙秘密,輪換。
- 配額/限制:fair-queue per tenant/endpoint,尊重「Retry-After」。
- Reconcile API和Checkpoints;訂戶文件。
- 可觀察性:p95/流/錯誤/DLQ,通過「event_id」跟蹤。
- 事件轉換和模式演變政策。
- 事件花花公子和「按鈕」全局暫停/解凍。
二.結論
可靠的webhooks是HTTP之上的協議,而不僅僅是「使用JSON的POST」。明確的合同(ID,訂單密鑰,簽名),相等性,帶有夾具的背包,公平的隊列和精心設計的花花公子將「最佳案例」轉換為可預測和可衡量的交付機制。按鍵+reconcile建立at-least-once+順序,系統將安靜地通過網絡、負載峰值和人為錯誤。