You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask服务导入requests库后停滞超时无响应问题咨询

场景说明

使用Python Flask服务对外提供远程数据库的数据访问服务,计划使用requests框架建立持久连接对接数据库。

使用技术栈
  • Python 3.10
  • Flask 2.0.3
  • Poetry 1.0(依赖管理工具)
  • Requests 2.28.1(通过Poetry添加依赖)
  • IBM Code Engine(云平台/运行环境)
异常现象

项目原本运行正常,当在server.py顶部添加import requests代码行后,服务无法返回任何响应,请求直接超时。导入其他部分框架时也会出现同类异常。

可复现代码

未添加import requests时可正常返回{ "hello": "there" }:

import os
import sys
import json
from collections import Counter
from flask import Flask, render_template, request, session, url_for, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def main():
    return { 'hello': "there" }

if __name__ == 'main':
    app.run()

添加import requests后服务卡顿无任何响应:

import os
import sys
import json
import requests # 新增导入行
from collections import Counter
from flask import Flask, render_template, request, session, url_for, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def main():
    return { 'hello': "there" }

if __name__ == 'main':
    app.run()

故障原因

核心是prefork工作模式下的锁状态复制异常,和业务逻辑无关:

  1. IBM Code Engine默认使用gunicorn作为WSGI服务器运行Flask应用,默认采用prefork工作模式:master进程先完成所有模块级代码的加载(包括顶层写的import requests),再fork出多个worker进程处理实际请求。
  2. requests依赖的urllib3在导入阶段就会初始化连接池相关的线程锁,fork操作会把父进程中已经初始化的锁状态原封不动复制到子进程,直接导致子进程内的锁处于不一致的错误状态。
  3. 当请求进入worker进程,只要触发urllib3相关的初始化逻辑(哪怕没有显式调用requests的接口,部分依赖的自检逻辑也会触发),就会永久阻塞在锁等待上,对外表现为所有请求超时无响应。
  4. 导入其他涉及网络连接、线程锁初始化的第三方框架时出现同类问题,触发逻辑完全一致。

另外代码中存在两个不影响本次故障、但会导致本地/自定义启动失败的错误:

  • 启动判断书写错误:if __name__ == 'main' 正确写法为 if __name__ == '__main__'(main两侧是双下划线)
  • 本地启动时没有指定监听地址和端口,不符合IBM Code Engine要求应用监听0.0.0.0:8080的规则。
修复方案

三选一即可,优先选择前两种生产可用方案:

方案1:配置gunicorn使用lazy加载模式

修改IBM Code Engine的启动命令为:

gunicorn --bind 0.0.0.0:8080 --workers 2 --lazy-apps server:app

--lazy-apps参数会让每个worker进程独立加载应用代码,而非master进程加载完再fork,从根源避免锁状态复制的问题,不需要修改任何业务代码。

方案2:延迟导入requests

不要在模块顶层导入requests,把导入逻辑放到实际使用的视图函数/工具函数内部,确保导入动作在worker进程启动完成后执行:

import os
import sys
import json
from collections import Counter
from flask import Flask, render_template, request, session, url_for, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def main():
    # 函数内部导入,worker启动完成后才执行
    import requests
    return { 'hello': "there" }

# 修正本地启动逻辑
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=False)

方案3:更换gunicorn的工作模式

把gunicorn的worker从默认的sync模式换成gevent/eventlet异步模式,这类模式不会走prefork的fork逻辑,也不会出现锁复制问题,启动命令示例:

gunicorn --bind 0.0.0.0:8080 --workers 2 --worker-class gevent server:app

注意使用该方案需要提前通过Poetry安装gevent依赖。


内容的提问来源于stack exchange,提问作者Brandon A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 09:15:43