Webhook回调重试与幂等设计实战概述Webhook传递在网络抖动与目标端故障下需具备去重、重试与回退能力,确保事件不丢与不重复处理。幂等键与去重窗口class IdempotencyStore { ttlMs: number map = new Map<string, number>() constructor(ttlMs: number) { this.ttlMs = ttlMs } set(key: string): void { this.map.set(key, Date.now() + this.ttlMs) } seen(key: string): boolean { const exp = this.map.get(key) if (!exp) return false if (Date.now() > exp) { this.map.delete(key); return false } return true } } function buildIdempotencyKey(eventId: string, bodyHash: string): string { return `${eventId}:${bodyHash}` } 指数退避重试async function retryWithBackoff(fn: () => Promise<Response>, maxAttempts: number, baseMs: number): Promise<Response> { let attempt = 0 while (attempt < maxAttempts) { const res = await fn() if (res.ok) return res attempt++ const wait = Math.min(30_000, baseMs * Math.pow(2, attempt)) await new Promise(r => setTimeout(r, wait)) } throw new Error("delivery_failed") } 状态码约定2xx:成功接收并完成处理409:幂等键重复,视为已处理429/503:建议重试,走退避策略4xx其他:不重试并记录失败发送端实现async function deliver(url: string, payload: string, idKey: string, store: IdempotencyStore): Promise<Response> { const bodyHash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload)) const key = buildIdempotencyKey(idKey, Array.from(new Uint8Array(bodyHash)).join("")) const fn = () => fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "X-Idempotency-Key": key }, body: payload }) return await retryWithBackoff(fn, 5, 500) } 接收端实现async function handleWebhook(req: { headers: Record<string, string>; body: string }, store: IdempotencyStore): Promise<{ status: number }> { const key = req.headers["x-idempotency-key"] if (!key) return { status: 400 } if (store.seen(key)) return { status: 409 } store.set(key) const ok = await processEvent(req.body) return { status: ok ? 200 : 503 } } 运维要点对重试次数、窗口命中与失败原因进行指标化与告警幂等键采用事件ID与内容哈希组合以抵御重复与篡改对重复事件保持幂等响应,避免副作用以上机制可在常见平台对接场景中提升Webhook传递的可靠性与安全性。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
1.991218s