GAE+gUnicorn+Flask单请求被双进程重复执行问题求助
GAE Python39环境下gUnicorn+Flask长请求被重复处理的问题排查与解决
问题场景
在Google AppEngine(Python39标准环境)部署了基于gUnicorn和Flask的应用,发起长耗时请求后出现重复处理问题:第一个worker进程运行1.5小时后,第二个worker启动并再次执行同一请求。浏览器DevTools显示仅发送了一次请求,但GAE日志最终出现两条请求记录,且traceId和requestId不同;两个进程并行执行导致内存压力增大、执行时间翻倍,第一个worker的结果无法返回客户端,需等待第二个worker完成。
相关代码与配置
Flask接口代码:
@app.route("/api/campaign/generate", methods=["GET"]) def campaign_generate(): logging.info('Entering campaign_generate'); # 长耗时处理逻辑
app.yaml配置:
runtime: python39 default_expiration: 0 instance_class: B2 basic_scaling: max_instances: 1 entrypoint: gunicorn -b :$PORT server.server:app --timeout 0 --workers 2
日志现象
13:00:58 GET /api/campaign/generate 13:00:59 Entering campaign_generate ..skipped 13:39:13 Starting generating zip-archive (it's something that takes a while) 14:25:49 Entering campaign_generate
问题原因
- GAE前端负载均衡(GFE)的超时重试机制:尽管设置了gUnicorn的
--timeout 0和基础缩放的24小时超时,但GFE存在默认请求超时阈值(通常约1小时)。当请求超过该阈值未返回响应时,GFE会判定第一个worker处理失败,自动重试请求并转发到同一实例的空闲worker进程,导致任务重复执行。 - GAE日志延迟上报特性:GAE的日志系统会延迟上报请求日志,第二次重试请求的日志会在整个请求完成后才统一展示,因此处理期间仅能看到第一条请求日志,事后才出现第二条。
- 多worker配置放大问题:由于配置了2个worker,GFE发起重试时存在空闲worker可以接收请求,直接导致同一任务被并行执行;本地环境无GFE重试机制,因此不会复现该问题。
解决方案
- 调整GFE请求超时配置:在
app.yaml的basic_scaling节点下添加request_timeout参数,设置超过GFE默认阈值的时长(如24小时,对应86400秒),覆盖GFE的默认超时规则:runtime: python39 default_expiration: 0 instance_class: B2 basic_scaling: max_instances: 1 request_timeout: 86400s # 设置为24小时 entrypoint: gunicorn -b :$PORT server.server:app --timeout 0 --workers 2 - 添加幂等性校验兜底:如果无法调整超时配置,可在Flask接口中添加幂等性逻辑,比如通过请求中的唯一标识(如用户ID+请求时间戳),在处理前检查任务是否已在执行,若已执行则直接返回结果或拒绝重复处理。
- 改用异步任务队列处理长任务:对于超过1小时的任务,更推荐使用GAE Cloud Tasks异步处理:前端仅提交任务请求,之后通过轮询或消息推送方式获取结果,彻底避免长时间占用请求连接,从根源上规避GFE超时重试问题。
内容的提问来源于stack exchange,提问作者Shrike
相关产品推荐
相关产品推荐

