Django项目第二次调用requests.post无异常抛出直接退出问题排查
问题触发原因
- 最初的
TypeError: getresponse() got an unexpected keyword argument 'buffering'是urllib3与Python标准库http.client版本不兼容导致的,旧版本urllib3调用getresponse()时传入了高版本Python才支持的buffering参数。 - 更新urllib3后出现的gunicorn worker超时退出,核心原因是第二次
requests.post()请求陷入无限阻塞,超过gunicorn的worker超时阈值后被进程管理器强制杀死:- requests默认没有设置全局超时时间,若目标服务响应慢或者TCP连接异常,请求会一直阻塞等待。
- requests默认开启HTTP长连接复用,第一次请求建立的长连接如果被服务端主动断开,第二次复用失效连接时会卡在TCP数据接收阶段,无错误抛出只会无限等待。
- 独立脚本运行正常是因为没有gunicorn的超时中断机制,即使请求阻塞也不会被强制杀死进程,最终能拿到响应或底层TCP超时抛出错误。
排查步骤
- 给所有requests请求添加显式超时参数,示例:
requests.post(url2, data=data2, timeout=10),若修改后抛出requests.exceptions.Timeout异常而非gunicorn worker退出,即可确认是请求无超时导致的阻塞。 - 给请求添加
Connection: close头禁用长连接复用,示例:requests.post(url2, data=data2, headers={"Connection": "close"}, timeout=10),若修改后请求正常返回,即可确认是长连接复用失效导致的问题。 - 临时调大gunicorn的超时阈值,启动时添加参数
--timeout 120,若调整后请求正常返回,说明目标接口响应时间超过了原有gunicorn超时配置。 - 在服务器本地用curl命令连续模拟两次POST请求,确认第二次请求的响应时间和返回结果是否正常,排除目标接口本身的故障。
解决方案
- 所有requests调用强制添加显式超时,建议分别设置连接超时和读取超时:
requests.post(url, data=data, timeout=(3, 10)),第一个参数是TCP连接超时时间,第二个是接口响应读取超时时间。 - 针对长连接复用失效问题,要么全局禁用requests长连接复用,要么每次请求使用独立的Session对象:
import requests # 方法1:禁用长连接 session = requests.Session() session.keep_alive = False res1 = session.post(url1, data=data1, timeout=(3,10)) result = res1.json() data2 = {'id': result['id']} res2 = session.post(url2, data=data2, timeout=(3,10)) session.close() # 方法2:每次请求新建连接,不复用连接池 res1 = requests.post(url1, data=data1, timeout=(3,10), headers={"Connection": "close"}) result = res1.json() data2 = {'id': result['id']} res2 = requests.post(url2, data=data2, timeout=(3,10), headers={"Connection": "close"})
- 若目标接口响应确实较长,优先将外部接口调用逻辑迁移到异步任务队列(如Celery)处理,避免阻塞gunicorn的HTTP请求进程;若业务必须同步等待结果,可适当调大gunicorn的超时配置,同时增加worker数量避免服务容量不足。
- 锁定依赖版本避免兼容性问题,推荐使用经过验证的版本组合:
requests==2.28.2、urllib3==1.26.15,可完全规避初始的buffering参数错误。
内容的提问来源于stack exchange,提问作者Basar
相关产品推荐
相关产品推荐

