关于Python脚本调用Everbridge API出现SSLEOFError导致请求重试超限的问题排查咨询
分析与解决方案:Everbridge API调用的SSLEOFError问题
看起来你遇到的是偶发的SSL连接中断错误,这类错误通常不是证书本身的问题(你已经确认了证书有效期和依赖版本),而是连接过程中的不稳定因素导致的。结合你的环境和代码,我整理了以下可能的原因和对应的解决思路:
可能的原因分析
- 代理服务的不稳定:你使用了Squid代理,这类错误很可能是代理在SSL握手或数据传输过程中意外断开了连接。比如代理临时过载、SSL协商不兼容,或者代理与Everbridge服务器之间的网络波动。
- 服务器端的临时连接中断:Everbridge API服务器可能在特定时刻因负载过高、临时维护或内部限流(即使你没触发公开的rate limit,也可能存在突发的连接限制)主动关闭了连接。
- SSL/TLS版本协商异常:你的OpenSSL版本是1.1.1s,虽然支持TLS 1.2和1.3,但某些情况下服务器或代理可能在版本协商时出现不兼容,导致连接提前终止。
- 会话复用的连接失效:你使用了
requests.session()来复用连接,但若服务器端主动关闭了空闲连接,客户端复用该连接时就会触发EOF错误。
具体解决方案
1. 添加智能重试机制
针对偶发的连接错误,给requests会话添加重试适配器,专门处理SSL相关错误和连接超时:
import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry proxies = {"http": squid_proxy_http, "https": squid_proxy_https} session = requests.session() # 配置重试策略:针对SSL错误、连接错误、5xx状态码重试 retry_strategy = Retry( total=3, backoff_factor=1, # 重试间隔:1s, 2s, 4s... status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["GET"], retry_on_ssl_errors=True ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("https://", adapter) session.mount("http://", adapter) # 后续请求保持你的原有代码,记得添加超时 BASE_URL = "https://api.everbridge.net/rest/incidents/123" HEADERS = { "Authorization": "", "accept": "application/json" } PARAMS = { "onlyOpen": "false", "pageNumber": 1, "pageSize": 200, "status": "All", "incidentType": "Incident", "startTime": start_epoch, "endTime": end_epoch } # 添加超时设置(连接超时30s,读取超时60s) first_response = session.get(BASE_URL, headers=HEADERS, params=PARAMS, proxies=proxies, timeout=(30, 60))
2. 排查代理服务问题
- 检查Squid代理的日志(Windows环境通常在安装路径的
logs文件夹下),查看错误发生时代理是否有SSL握手失败、连接超时的记录。 - 如果允许,临时绕过代理直接请求Everbridge API,观察是否还会出现错误,以此确认是否是代理导致的问题。
3. 强制指定SSL/TLS版本
如果是版本协商问题,可以强制客户端使用特定的TLS版本,比如TLS 1.2(Everbridge服务器通常兼容这个版本):
import ssl from requests.adapters import HTTPAdapter from urllib3.poolmanager import PoolManager class TLSAdapter(HTTPAdapter): def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, ssl_version=ssl.PROTOCOL_TLSv1_2 ) # 替换之前的适配器,使用自定义的TLSAdapter session.mount("https://", TLSAdapter())
4. 优化会话连接复用
如果是连接复用导致的失效,可以尝试:
- 调整连接池参数,限制连接池大小,避免长时间空闲的连接:
adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=5, pool_maxsize=10) - 或者在分页循环中,若遇到错误则重置会话:
try: response = session.get(BASE_URL, headers=HEADERS, params=page_params, proxies=proxies, timeout=(30, 60)) data = response.json() except Exception as e: # 重置会话后重试 session = requests.session() session.mount("https://", adapter) response = session.get(BASE_URL, headers=HEADERS, params=page_params, proxies=proxies, timeout=(30, 60)) data = response.json()
5. 升级依赖库
虽然你的依赖版本不算过时,但可以尝试升级到最新版,修复可能存在的SSL相关bug:
pip install --upgrade requests urllib3 certifi
6. 添加详细日志
在脚本中添加日志记录,方便排查错误发生时的上下文:
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # 在分页循环中添加日志 while current_pageno <= total_pages: page_params = PARAMS.copy() page_params["pageNumber"] = current_pageno logging.info(f"开始请求第 {current_pageno} 页,时间范围: {start_epoch} 至 {end_epoch}") try: response = session.get(BASE_URL, headers=HEADERS, params=page_params, proxies=proxies, timeout=(30, 60)) response.raise_for_status() data = response.json() logging.info(f"第 {current_pageno} 页请求成功,返回数据条数: {len(data.get('incidents', []))}") # 处理数据逻辑... current_pageno += 1 except Exception as e: logging.error(f"第 {current_pageno} 页请求失败: {str(e)}") # 可根据需要添加重试逻辑 raise
内容的提问来源于stack exchange,提问作者SC_Python




