news 2026/9/16 19:53:53

Python requests库网络请求卡死问题分析与解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python requests库网络请求卡死问题分析与解决方案

1. 问题现象与根源分析

当使用Python的requests库进行网络请求时,经常会遇到程序卡死无响应的情况。这种问题通常表现为:

  • 程序长时间挂起不返回结果
  • 控制台无任何错误输出
  • 最终可能抛出requests.exceptions.Timeout异常
  • 在极端情况下甚至会导致整个脚本进程僵死

造成这种问题的核心原因主要有以下几个方面:

1.1 网络层超时未设置

requests库默认不设置超时参数,这意味着如果服务器不响应,你的程序将无限期等待。这是大多数卡死问题的直接原因。根据HTTP协议规范,客户端应当始终设置合理的超时时间。

重要提示:生产环境中永远不要使用无超时的请求,这会导致资源泄漏和系统不稳定。

1.2 连接池资源耗尽

当高频发起请求且未正确关闭连接时,TCP连接会堆积在TIME_WAIT状态。典型的症状是:

  • 前几百个请求正常
  • 随后逐渐变慢
  • 最终完全卡死
  • 伴随"Too many open files"错误

1.3 服务端限制触发

从热词中可以看到大量"429 Too Many Requests"和"rate limit"相关错误。当服务端实施限流策略时,可能出现:

  • 请求被静默丢弃
  • 返回非标准错误码
  • 故意延迟响应

1.4 DNS解析问题

DNS查询默认也是无超时的,可能因为:

  • 本地DNS服务器故障
  • 域名记录配置错误
  • 网络策略限制

2. 全方位解决方案

2.1 基础超时设置

requests库支持多层超时控制,最佳实践是同时设置连接超时和读取超时:

import requests # 推荐设置方式(连接超时3秒,读取超时30秒) response = requests.get( 'https://api.example.com/data', timeout=(3, 30) # (connect_timeout, read_timeout) )

参数选择依据:

  • 内网服务:连接(1s), 读取(5s)
  • 公网API:连接(3s), 读取(30s)
  • 文件下载:连接(5s), 读取(300s)

2.2 高级重试机制

对于不稳定的网络环境,需要实现智能重试:

from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session() retry_strategy = Retry( total=3, # 最大重试次数 backoff_factor=1, # 指数退避因子 status_forcelist=[408, 429, 500, 502, 503, 504] ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) try: response = session.get('https://api.example.com', timeout=(3, 30)) except requests.exceptions.RequestException as e: print(f"最终请求失败: {e}")

2.3 连接池优化配置

防止资源泄漏的完整方案:

import requests from requests.adapters import HTTPAdapter session = requests.Session() # 连接池配置 adapter = HTTPAdapter( pool_connections=20, # 连接池大小 pool_maxsize=100, # 最大连接数 max_retries=3 # 每个连接的重试次数 ) session.mount('http://', adapter) session.mount('https://', adapter) # 使用后确保关闭 try: response = session.get('https://api.example.com', timeout=(3, 30)) finally: session.close() # 关键!释放所有连接

2.4 DNS缓存优化

解决DNS解析问题的两种方案:

方案1:使用静态hosts

import socket socket.gethostbyname('api.example.com') # 提前解析并缓存

方案2:自定义DNS解析

from urllib3.util import connection _orig_create_connection = connection.create_connection def patched_create_connection(address, *args, **kwargs): host, port = address if host == 'api.example.com': host = '1.2.3.4' # 替换为已知IP return _orig_create_connection((host, port), *args, **kwargs) connection.create_connection = patched_create_connection

3. 特殊场景处理

3.1 处理429 Too Many Requests

当遇到限流时(如热词中的"exceeded retry limit"),应当:

  1. 解析Retry-After头部
  2. 实现令牌桶算法
  3. 添加随机延迟
import random import time def make_request(url): try: response = requests.get(url, timeout=(3, 30)) if response.status_code == 429: retry_after = int(response.headers.get('Retry-After', 5)) time.sleep(retry_after + random.uniform(0, 1)) return make_request(url) # 递归重试 return response except requests.exceptions.RequestException: time.sleep(5 + random.random()) return make_request(url)

3.2 大文件下载不断流

下载大文件时的优化方案:

import requests url = "https://example.com/large_file.zip" local_filename = "downloaded_file.zip" with requests.get(url, stream=True, timeout=(5, 300)) as r: r.raise_for_status() with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): if chunk: # 过滤keep-alive空chunk f.write(chunk) f.flush()

关键参数:

  • stream=True:不立即下载完整内容
  • chunk_size=8192:8KB的块大小
  • 显式调用flush():防止写入缓存导致内存增长

4. 调试与问题排查

4.1 请求日志记录

启用详细日志记录:

import logging import http.client http.client.HTTPConnection.debuglevel = 1 logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) requests_log.propagate = True

4.2 常见错误代码处理

完整的状态码处理方案:

def handle_response(response): if 200 <= response.status_code < 300: return response.json() elif response.status_code == 400: raise ValueError("Bad Request") elif response.status_code == 401: raise PermissionError("Unauthorized") elif response.status_code == 403: raise PermissionError("Forbidden") elif response.status_code == 404: raise FileNotFoundError("Not Found") elif response.status_code == 429: raise RuntimeError("Rate Limited") elif 500 <= response.status_code < 600: raise ConnectionError(f"Server Error: {response.status_code}") else: raise RuntimeError(f"Unexpected status: {response.status_code}")

4.3 网络层问题诊断

使用底层工具检查连接状态:

import socket def check_connection(host, port, timeout=3): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(timeout) try: s.connect((host, port)) return True except socket.error: return False finally: s.close()

5. 生产环境最佳实践

5.1 全局默认超时设置

避免忘记设置timeout的终极方案:

import requests class SafeSession(requests.Session): def request(self, *args, **kwargs): if kwargs.get('timeout') is None: kwargs['timeout'] = (3, 30) # 默认超时 return super().request(*args, **kwargs) # 使用自定义Session session = SafeSession() response = session.get('https://api.example.com') # 自动应用超时

5.2 异步请求模式

对于高并发场景,使用aiohttp:

import aiohttp import asyncio async def fetch(session, url): try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as response: return await response.text() except asyncio.TimeoutError: print(f"Timeout on {url}") return None async def main(): connector = aiohttp.TCPConnector(limit=100) # 连接池限制 async with aiohttp.ClientSession(connector=connector) as session: tasks = [fetch(session, f"https://api.example.com/items/{i}") for i in range(100)] await asyncio.gather(*tasks) asyncio.run(main())

5.3 熔断器模式实现

防止级联故障的解决方案:

from circuitbreaker import circuit @circuit(failure_threshold=5, recovery_timeout=60) def make_api_request(url): response = requests.get(url, timeout=(3, 30)) response.raise_for_status() return response.json()

关键参数:

  • failure_threshold:连续失败次数阈值
  • recovery_timeout:熔断恢复时间(秒)

6. 性能优化技巧

6.1 连接复用优化

保持长连接的配置方法:

import requests session = requests.Session() # 保持连接活跃 adapter = requests.adapters.HTTPAdapter( pool_connections=10, pool_maxsize=100, pool_block=True # 当连接池满时等待而非创建新连接 ) session.mount('http://', adapter) session.mount('https://', adapter) # 添加Keep-Alive头 session.headers.update({'Connection': 'keep-alive'})

6.2 TCP优化参数

底层socket参数调优:

import socket from urllib3.connection import HTTPConnection # 调整TCP参数 HTTPConnection.default_socket_options = ( HTTPConnection.default_socket_options + [ (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30), (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60), ] )

6.3 内存管理技巧

处理大响应时的内存优化:

import requests from io import BytesIO url = "https://example.com/large_data" response = requests.get(url, stream=True) buffer = BytesIO() for chunk in response.iter_content(chunk_size=1024): if chunk: # 过滤keep-alive空chunk buffer.write(chunk) if buffer.tell() > 10 * 1024 * 1024: # 10MB内存限制 process_data(buffer.getvalue()) buffer.seek(0) buffer.truncate() # 处理剩余数据 if buffer.tell() > 0: process_data(buffer.getvalue())

7. 疑难问题解决方案

7.1 代理环境问题

正确处理代理设置:

import requests proxies = { 'http': 'http://proxy.example.com:8080', 'https': 'http://proxy.example.com:8080', } # 带超时的代理请求 try: response = requests.get( 'https://api.example.com', proxies=proxies, timeout=(10, 30) # 代理环境下适当延长超时 ) except requests.exceptions.ProxyError: print("代理连接失败") except requests.exceptions.SSLError: print("SSL证书验证失败")

7.2 SSL证书问题

灵活处理证书验证:

import requests import ssl # 方案1:关闭验证(不推荐) response = requests.get('https://api.example.com', verify=False) # 方案2:使用自定义CA包 response = requests.get( 'https://api.example.com', verify='/path/to/cert.pem' ) # 方案3:调整SSL协议版本 ssl_context = ssl.create_default_context() ssl_context.options |= ssl.OP_NO_SSLv2 ssl_context.options |= ssl.OP_NO_SSLv3 session = requests.Session() session.verify = True session.mount('https://', requests.adapters.HTTPAdapter(max_retries=3, ssl_context=ssl_context))

7.3 连接泄漏检测

使用资源跟踪检测泄漏:

import requests import weakref import gc class TrackedSession(requests.Session): _instances = set() def __init__(self): super().__init__() self.__class__._instances.add(weakref.ref(self)) @classmethod def check_leaks(cls): gc.collect() return sum(1 for ref in cls._instances if ref() is not None) # 使用示例 session = TrackedSession() response = session.get('https://api.example.com') print(f"活跃Session数量: {TrackedSession.check_leaks()}")
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:52:25

51单片机+Proteus仿真:多功能断路器开发设计全解析

简介&#xff1a;基于51单片机Proteus仿真的多功能断路器开发设计资料包&#xff0c;主要面向单片机初学者、电子类课程设计学生以及嵌入式项目开发者。资源围绕智能断路器展开&#xff0c;能够实现电流、电压、温度的实时检测&#xff0c;过压、欠压、过流、过热判断与报警控制…

作者头像 李华
网站建设 2026/9/16 19:51:25

CVAT自动标注+YOLOv5部署实战:从nuctl安装到模型调用全流程

先放结论&#xff1a;CVAT自动标注加YOLOv5这套组合&#xff0c;目前仍然是开源工具链里最能打的半自动标注方案&#xff0c;没有之一。但整个部署过程远没有官方文档看起来那么平顺&#xff0c;我自己在从零搭建这套流程时&#xff0c;光是nuctl这一关就折腾了大半天&#xff…

作者头像 李华