Python长耗时POST请求服务端已返回但requests.post仍阻塞问题咨询
问题原因与解决方案
根因分析
- 最核心的诱因是AWS NAT网关的默认空闲连接超时阈值为350秒(约5分50秒),刚好接近你请求的正常处理耗时。高负载场景下服务端处理耗时稍微波动,或者网络链路有轻微延迟,就会导致空闲连接超过350秒没有数据包交互,NAT网关会静默丢弃该连接,且不会向两端发送连接断开的通知。此时服务端虽然已经生成200响应并尝试回包,但对应的TCP连接已经被中间链路掐断,客户端收不到响应数据,会一直阻塞直到你设置的720秒超时触发重试。
- 其次是requests默认未开启TCP keepalive,无法在连接空闲时发送探测包维持长连接的活跃状态,也无法及时感知中间链路的连接断开。
- 你当前传递的
timeout=720参数存在歧义:requests的timeout参数如果传入单个数值,会同时作用于连接超时和读取超时,而读取超时定义的是两次相邻数据包接收的间隔时长,并非总请求时长,极端场景下可能不符合你的预期。
修复方案
- 调整AWS NAT网关的空闲连接超时阈值,将其设置为大于720秒的数值(建议设为900秒即15分钟),避免中间链路主动断开正常处理中的长连接。如果你的链路中还存在负载均衡、反向代理等中间设备,也要同步调整对应设备的空闲连接超时配置。
- 为requests开启TCP keepalive配置,通过自定义HTTP适配器设置TCP保活参数,定期发送探测包维持连接活跃,同时可以及时感知链路断开。示例代码如下:
import requests from requests.adapters import HTTPAdapter class TCPKeepAliveAdapter(HTTPAdapter): def init_poolmanager(self, *args, **kwargs): socket_options = [ *self.default_socket_options, # 开启TCP保活 ("SO_KEEPALIVE", 1), # 连接空闲120秒后开始发送探测包 ("TCP_KEEPIDLE", 120), # 探测包发送间隔30秒 ("TCP_KEEPINTVL", 30), # 最多发送5次探测无响应则判定连接断开 ("TCP_KEEPCNT", 5), ] kwargs["socket_options"] = socket_options super().init_poolmanager(*args, **kwargs) # 使用时创建会话挂载适配器 session = requests.Session() session.mount("http://", TCPKeepAliveAdapter()) session.mount("https://", TCPKeepAliveAdapter()) response = session.post( url=url, params=params, headers=self.headers, json=payload, # 明确指定连接超时30秒,读取超时720秒,避免参数歧义 timeout=(30, 720) )
- 长期优化可以考虑将长耗时POST请求改为异步处理模式:服务端收到请求后先返回任务ID,客户端定期轮询任务结果,避免长连接带来的各类链路稳定性问题。
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

