本文目录导读:

Webhook 本身没有自动重试机制,但许多 Webhook 的发送方(如 GitHub、Stripe、企业微信等)会在事件投递失败时提供自动重试功能。
具体实现取决于谁在发送 Webhook以及后端如何接收,以下是几种常见情况及实现方法:
依赖 Webhook 提供方自带的自动重试机制
大多数主流 Webhook 服务商都内置了重试策略。
- 工作原理:当你的服务器返回
5xx(服务器错误)或超时(> 5秒)或返回特定的4xx时,发送方会稍后再次发送相同的 Webhook 请求。 - 典型策略:
- 指数退避:第一次失败后等1分钟,第二次等5分钟,第三次等30分钟……直到达到最大重试次数(如3-10次)。
- 固定间隔:例如每15分钟重试一次,共重试24小时。
- 常见例子:
- GitHub:返回非
2xx或超时则重试,共重试3次,间隔约30秒、2分钟、5分钟。 - Stripe:会重试最多3天,间隔逐渐增加。
- 支付宝/微信支付:通常有异步通知机制,会重试8-12次,间隔递增(例如2m, 10m, 10m, 1h, 2h, 6h, 15h)。
- GitHub:返回非
你只需要:
- 确保你的服务器端接收端点同步处理逻辑(收到请求 -> 处理成功 -> 立即返回
200 OK给发送方)。 - 如果处理失败(如数据库挂了),返回非
2xx状态码(如500或503),发送方就会自动重试。
手动实现或增强重试逻辑(当提供方不做重试时)
如果你使用的 Webhook 发送方没有内置重试,你需要自己动手,常见方案:
方案 A:使用消息队列 + 失败重试(推荐)
这是最稳健的做法,脚本不直接处理 Webhook,而是把事件存起来。
- 接收请求:脚本收到 Webhook 后,立即返回
200 OK。 - 写入队列:把事件数据写入一个可靠的队列(如 Redis List, RabbitMQ, AWS SQS)或数据库(如 MySQL)。
- 异步处理:另一个 Worker 脚本不断从队列中拉取事件进行处理。
- 重试逻辑:Worker 处理失败,它不会丢弃消息,而是:
- 将消息重新放入队列(延迟队列)。
- 更新数据库中的重试计数。
- 假设最大重试3次,指数退避(1分钟 -> 5分钟 -> 30分钟)。
- 超过最大次数,移入死信队列供人工排查。
伪代码示例(Python + Redis + 简单循环):
import redis, time, json
r = redis.Redis()
MAX_RETRIES = 3
def process_webhook(event_data):
# 真正的业务逻辑,例如给用户发邮件
# 如果失败,抛出异常 or return False
pass
def worker():
while True:
# 从阻塞队列取消息,起一个便于重试的名字
item = r.blpop("webhook_queue", timeout=0)
data = json.loads(item[1])
retries = data.get('retries', 0)
try:
process_webhook(data['payload'])
print("处理成功")
except Exception as e:
print(f"处理失败: {e}")
if retries < MAX_RETRIES:
data['retries'] = retries + 1
# 指数退避延迟:2^retries * 60 秒
delay = (2 ** retries) * 60
# 放入延迟队列(需要自己实现或使用Redis的ZSet)
# 简单实现:先睡一会再放回主队列(不推荐但能用)
time.sleep(delay)
r.lpush("webhook_queue", json.dumps(data))
print(f"放入重试队列, 第{retries+1}次")
else:
# 超过次数,记录到死信日志
r.lpush("webhook_dead_letter", json.dumps(data))
print("超过重试次数,记录死信")
方案 B:使用定时任务(Cron Job)轮询
如果你用 Spring Boot Scheduler 或 Linux Cron。
- 脚本不直接处理实时事件。
- 将事件存入数据库(
webhook_events表),状态为pending。 - 定时任务(如每5分钟执行一次)扫描
status = pending AND retries < 3的记录。 - 尝试处理,成功则更新状态为
completed;失败则retries + 1,并根据last_attempt_at判断是否已达到下次重试时间(指数退避)。
关键注意事项(避免事故)
| 问题 | 解决方案 |
|---|---|
| 重复事件 | 提供方重试、MQ重试都可能导致同一个事件被处理多次。你的脚本必须是幂等的(通过 event_id 去重,如果已处理则直接返回成功)。 |
| 超时 | 如果业务处理需要很长时间(如生成报告),不要直接在 Webhook 回调里做。先返回 202 Accepted,然后异步处理。 |
| 死信 | 所有重试方案都必须有最终兜底,当重试次数用完后,通知管理员或写入专门的文件/队列,避免数据丢失。 |
| 请求验证 | 重试会重复发送相同的请求,务必验证签名(如 HMAC-SHA256)或 IP 白名单,防止被伪造。 |
- 对于主流服务(GitHub, Stripe, 微信支付):你的脚本不需要写重试逻辑,只需要确保处理失败时返回错误码(
5xx),它们会自动重试。这是最省事的做法。 - 对于自建系统或无重试的发件方:你需要用消息队列+死信机制来手动实现指数退避重试,这是最可靠的架构,避免直接在脚本里写
while循环sleep。
你的场景是哪种发件方?如果是通用场景,建议把“返回错误码让提供方重试”作为第一选择。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播