多Gunicorn Worker部署Flask API出现重复请求的原因及解决方法
问题分析与解决方案
先澄清:Gunicorn默认逻辑下不会重复分发请求
Gunicorn的master进程负责监听端口、接收请求,然后将请求转发给空闲的worker进程,正常情况下同一个请求只会被一个worker处理。你碰到的重复请求,大概率不是Gunicorn的worker重复处理导致的,更可能来自这些场景:
- 客户端(比如提交表单的前端、调用方)因为网络波动、超时自动重试
- 用户多次点击提交按钮,前端触发重复请求
- 上游有负载均衡/反向代理时,代理因为没收到响应超时,重新转发了请求
- 你的接口调用第三方API时,第三方返回超时但实际已处理,你这边如果有重试逻辑就会触发重复操作
极端情况下,如果worker处理请求时崩溃,master可能会重新分发请求,但这种情况很少见。
最根本的解决:给接口加幂等性
不管重复请求来自哪里,最可靠的解决方式是让/endpoints/create_parcel接口实现幂等性——也就是同一个请求(或相同业务逻辑的请求)调用多少次,都只会产生一次有效结果。具体可以这么做:
- 要求客户端传唯一标识:让提交请求的一方携带一个
request_id(比如UUID),接口处理前先查这个ID是否已经处理过(存在数据库或Redis里),如果已经处理过,直接返回之前的结果,不再调用第三方API - 用业务参数生成唯一键:如果客户端没法传
request_id,可以用用户ID、包裹内容哈希、提交时间戳这类参数组合成唯一键,同样先检查键是否存在,避免重复执行 - 利用第三方API的幂等机制:如果第三方API支持,调用时传他们要求的幂等键(比如
idempotency_key),确保重复调用不会重复创建资源
针对Gunicorn的排查与调整
如果你还是怀疑Gunicorn的问题,可以做这些操作:
- 查worker超时日志:你设置了
--timeout 300,如果请求处理时间接近或超过这个值,worker会被master杀死,master可能会重新分发请求。去看Gunicorn的日志(默认输出到stderr),有没有worker超时重启的记录 - 换异步worker模式:默认的
sync模式在处理阻塞操作(比如调用第三方API)时,worker会被占住。可以试试gevent或eventlet异步模式,减少阻塞导致的请求排队和异常分发:# 先安装gevent:pip install gevent /home/user/miniconda3/bin/gunicorn --bind 0.0.0.0:5000 --workers 3 --worker-class gevent --timeout 300 --chdir /home/user/app wsgi:app - 开访问日志排查:启动Gunicorn时加
--access-logfile -把访问日志打在控制台,或者指定日志文件,看看同一请求的标识(如果有的话)是不是被多次记录,确认是不是Gunicorn分发了重复请求
其他排查方向
- 检查前端防抖:确认表单提交有没有做防抖处理,防止用户多点导致重复请求
- 查反向代理配置:如果用了Nginx之类的代理,看代理的超时和重试设置,比如
proxy_next_upstream是不是开了不必要的重试 - 核对第三方API日志:联系第三方确认他们是不是收到了重复调用,定位请求重复的发起方
内容的提问来源于stack exchange,提问作者Erlend Davidson
相关产品推荐
相关产品推荐

