Webhooks:リプレイと謝辞
1)基本的な配達モデル
At-less-once(デフォルト)-イベントは1回≥配信されます。正確に一度の保証は受信機のidempotencyによって達成されます。
Acknowledgement (ACK):受信者からの2xx(通常は200/204)のみが成功を意味します。他のすべては失敗として解釈され、繰り返しにつながります。
高速ACK:完全なビジネス処理の後ではなく、順番にイベントを配置した後に2xxに応答します。
2)イベントフォーマットと必須見出し
ペイロード(例)
json
{
"id": "evt_01HXYZ",
"type": "order. created",
"occurred_at": "2025-11-03T18:10:12Z",
"sequence": 128374,
"source": "orders",
"data": { "order_id": "o_123", "amount": "49. 90", "currency": "EUR" },
"schema_version": 1
}
送信ヘッダー
'X-Webhook-Id: evt_01HXYZ'-一意のイベントID(重複除外に使用)。
'X-Webhook-Seq: 128374'-モノトーンシーケンス(サブスクリプション/テーマ別)。
'X-Signature: sha256=<base64 (hmac_sha256 (body、 secret))>'-HMAC- подпись。
'X-Retry: 0,1,2……'は試行番号です。
'X-Webhook-Version: 1'-契約バージョン管理。
(オプション)'Traceparent'-トレース相関。
受信者からの応答
2xx-正常に受け入れられました(この'id'にはそれ以上の繰り返しはありません)。
410 Gone-エンドポイントが削除/非アクティブ→送信者は再起動を終了し、サブスクリプションを無効にします。
429/5xx/timeout-送信者はリトレイポリシーに従って繰り返します。
3)再利用ポリシー
推奨バックオフラダー(+ジッター)
'1s、 3s、 10s、 30s、 2m、 10m、 30m、 2h、 6h、 24h'(限界の後で、例えば48-72時間)。
ルール:- 指数関数バックオフ+ランダムジッタ(± 20-30%)「群れ効果」を避ける。
- 一時的な障害のためのエラーのクォーラム(たとえば、5xxまたはネットワークタイムアウトの場合は再試行)。
- Respect 429:最小'min (Retry-Afterヘッダー、次のバックオフウィンドウ)'を設定します。
タイムアウトとサイズ
接続タイムアウト≤ 3-5秒。合計応答タイムアウト≤ 10秒
契約の下の本体のサイズ(例えば、≤ 256 KB)、そうでなければ413→ロジック「chunking」または「pull URL」。
4) Idempotencyと重複除外
Idempotent application:同じ'id'の処理の繰り返しは同じ結果を返す必要があります。
受信者側のデダップストレージ:ストア'(X-Webhook-Id、 processed_at、チェックサム)'TTL ≥リトレイウィンドウで(24-72時間)。
構成キー:複数のトピック→'(subscription_id、 event_id)'の場合。
5)順序および「丁度一度の効果」
分散システムの厳格な秩序を保証することは困難です。使用して下さい:- キーによるパーティション:同じ論理セット(例えば、'order_id')は常に配信の1つの「チャネル」にあります。
- シーケンス:古い「X-Webhook-Seq」でイベントを拒否し、行方不明のイベントが到着する前に「駐車場」に入れます。
- 適用された操作のログ(outbox/inboxパターン)、
- データベース内の'event_id'によるトランザクションのupsert、
- 複雑なプロセスのサガ/補償。
6)ステータスコードによるエラー解決(表)
7)チャネルの保証
各メッセージのHMAC署名。「タイムウィンドウ」(ミットとリプレイ攻撃)で受信機で確認します。
機密ドメイン(LCC/決済)のmTLS。
発信アドレスのIP allowlist、 TLS 1。2+、HSTS。
PII最小化:不要な個人データを送信しないでください。ログに偽装します。
シークレットの回転:2つの有効なキー(active/next)と'X-Key-Id'ヘッダーが現在のキーを示します。
8)キュー、DLQ、リプレイ
イベントは送信側の出力キュー/ログに書き込まれる必要があります(信頼性の高いリプレイのため)。
リトレイの最大値を超えた場合、イベントは原因とともにDLQ (Dead Letter Queue)に移動します。
Replay API(受信者/演算子用):RPS制限と追加の署名/承認を備えた'id'/時間範囲/件名で再送信します。
POST /v1/webhooks/replay
{ "subscription_id": "sub_123", "from": "2025-11-03T00:00:00Z", "to": "2025-11-03T12:00:00Z" }
→ 202 Accepted
9)契約とバージョン
イベント('schema_version'フィールド)とトランスポート('X-Webhook-Version')をバージョン化します。
オプションとしてのみフィールドを追加します。削除-マイナーな移行と移行期間(デュアルライト)。
イベントタイプ、例、スキーマ(JSONスキーマ)、エラーコードを文書化します。
10)観察可能性およびSLO
送信者の主な指標:- 'delivery_success_rate' (2xx/すべての試行)、'first_attempt_success_rate'
- 'retries_total'、 'max_retry_age_seconds'、 'dlq_count'
- 'latency_p50/p95' (occurred_at→ack_received_at)
- 'ak_latency'(受信→2xx)、 'processing_latency' (enqueue→done)
- 'duplicates_total'、 'invalid_signature_total'、 'out_of_order_total'
99.イベントの9%が最初のACK ≤ 60秒(28d)を受信します。
- DLQ ≤ 0。全体の1%;DLQ再生≤ 24時間です。
11)タイミングおよびネットワーク休憩
タイムフィールドでUTCを使用します。NTPを同期します。
'occorded_at'を送信し、'delivered_at'を修正してラグを読み込みます。
長い休憩では、ネットワーク/エンドポイント→キューに蓄積し、成長を制限します(バックプレッシャー+クォータ)。
12)推薦された限界および衛生
サブスクリプションあたりのRPS(例: 50 RPS、バースト100)+並行(例:10).
マックス・マックス。ボディ:64-256 KB;詳細については「notification+URL」と署名をダウンロードしてください。
「ヘビ」のイベント名。case'または'dot。type'('order。')を作成しました。
受信者の書き込み操作の厳密なidempotency。
13)例: 送信者と受信者
13.1送信者(擬似コード)
python def send_event(event, attempt=0):
body = json. dumps(event)
sig = hmac_sha256_base64(body, secret)
headers = {
"X-Webhook-Id": event["id"],
"X-Webhook-Seq": str(event["sequence"]),
"X-Retry": str(attempt),
"X-Signature": f"sha256={sig}",
"Content-Type": "application/json"
}
res = http. post(endpoint, body, headers, timeout=10)
if 200 <= res. status < 300:
mark_delivered(event["id"])
elif res. status == 410:
deactivate_subscription()
else:
schedule_retry(event, attempt+1) # backoff + jitter, respect 429 Retry-After
13.2受信機(擬似コード)
python
@app. post("/webhooks")
def handle():
body = request. data headers = request. headers assert verify_hmac(body, headers["X-Signature"], secret)
evt_id = headers["X-Webhook-Id"]
if dedup_store. exists(evt_id):
return, "" 204 enqueue_for_processing (body) # fast path. dedup_store put(evt_id, ttl=723600)
return, "" 202 # or 204
14)テストとカオスの実践
負の場合:無効な署名、429/5xx、タイムアウト、410、大きなペイロード。
行動:アウトオブオーダー、重複、1〜10分の遅延、24時間の休憩。
負荷:破烈10 ×;backpressureとDLQの持続性をチェックします。
契約:JSONスキーマ、必須の見出し、安定したイベントタイプ。
15)実装チェックリスト
- 2xx=ACK、およびenqueue後のクイックリターン
- 指数関数バックオフ+ジッタ、リスペクト'Retry-After'
- 受信者IDempotencyとX-Webhook-Id (TTL ≥リトレイ)
- HMAC署名、シークレット回転、オプションのmTLS
- DLQ+リプレイAPI、監視およびアラート
- 制限:タイムアウト、RPS、ボディサイズ
- 注文:キーまたは'sequence'+「駐車場」によるパーティション"
- ドキュメント:スキーマ、例、エラーコード、バージョン
- カオステスト:遅延、重複、ネットワーク障害、長いリプレイ
16) ミニFAQ
私は常に200に答える必要がありますか?
すべての2xxは成功としてカウントされます。202/204は「キューに受け入れられる」ための通常の練習です。
リプレイは停止できますか?
はい、410応答および/または送信者のコンソール/API(購読解除)を介して。
大きなペイロードはどうですか?
「notification+secure URL」を送信し、ダウンロードリクエストに署名してTTLをインストールします。
順序を保障する方法か?
キー+'シーケンス'によるパーティション;不一致の場合-「駐車場」とリプレイ。
合計
信頼できるWebhookは明確なACK (2xx)の意味論、backoff+jitterの適度な繰り返し、厳密なidempotenceおよび重複除外、有能な保証(HMAC/mTLS)、キュー+DLQ+リプレイおよび透明な観察可能性です。契約を修正し、限界とメトリクスを入力し、定期的にカオスシナリオを実行します。統合は、最初の失敗時に「注入」を停止します。