如何用Python Requests解决AWS Fargate中的TCP连接重置问题?
问题描述
在AWS Fargate上使用Amazon ECS时,实例可正常访问互联网,但连接会在350秒后断开。平均每100次调用中,服务约5次出现ConnectionResetError: [Errno 104] Connection reset by peer错误。
问题原因
使用NAT网关的连接若闲置350秒或更久,会触发超时。超时后,NAT网关会向后方尝试继续连接的资源返回RST包(不会发送FIN包),导致连接重置错误。
现有临时处理方式
目前使用tenacity实现了重试逻辑作为权宜之计:
@retry( retry=( retry_if_not_exception_type( HTTPError ) # 针对requests.exceptions.ConnectionError ), reraise=True, wait=wait_fixed(2), stop=stop_after_attempt(5), ) def call_to_api(): url = "要调用的HTTP地址" params = { "year": year, "month": month } response = self.session.get(url, params=params)
基础请求代码:
url = "要调用的HTTP地址" params = { "year": year, "month": month } response = self.session.get(url, params=params)
核心需求
如何用Python requests实现以下任一方案:
- 在闲置350秒前关闭连接
- 启用TCP连接的Keep-Alive
方案一:闲置350秒前关闭连接
requests默认会复用连接,可通过以下方式主动关闭连接:
- 手动关闭响应对象:处理完响应后直接关闭
response = self.session.get(url, params=params) # 处理响应逻辑 response.close()
- 使用上下文管理器自动关闭:离开代码块后自动释放连接
with self.session.get(url, params=params) as response: # 处理响应内容,比如解析JSON data = response.json() # 离开with块后连接自动关闭
- 禁用连接复用(不推荐,会增加连接建立开销):通过适配器配置强制每次请求后关闭连接
from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry adapter = HTTPAdapter( pool_connections=10, pool_maxsize=10, pool_block=False, max_retries=Retry(total=0) ) self.session.mount("http://", adapter) self.session.mount("https://", adapter) # 后续请求不再复用连接 response = self.session.get(url, params=params)
方案二:启用TCP Keep-Alive并设置超时小于350秒
requests本身不直接暴露TCP keepalive配置,需通过底层urllib3实现:
方式1:自定义会话适配器(推荐,仅作用于目标会话)
import requests from requests.adapters import HTTPAdapter from urllib3 import PoolManager import socket class KeepAliveAdapter(HTTPAdapter): def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, socket_options=[ (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), # 闲置300秒后开始发送keepalive探测(小于350秒) (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 300), # 每次探测间隔60秒 (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 60), # 最多探测5次 (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) ] ) # 给会话挂载自定义适配器 self.session = requests.Session() self.session.mount("http://", KeepAliveAdapter()) self.session.mount("https://", KeepAliveAdapter()) # 正常发起请求 response = self.session.get(url, params=params)
方式2:全局设置Socket选项(作用于所有Python进程的连接)
import socket # 全局启用TCP keepalive socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 300) socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 60) socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) # 后续使用requests会话发起请求 response = self.session.get(url, params=params)
补充建议
- 优先选择启用TCP Keep-Alive的方案,既能保留连接复用的性能优势,又能避免NAT网关的超时重置
- 若选择关闭连接的方案,需注意高并发场景下连接建立的额外开销
- 保留tenacity的重试逻辑作为兜底,极端场景下仍可能出现连接重置
内容的提问来源于stack exchange,提问作者A l w a y s S u n n y
相关产品推荐
相关产品推荐

