You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于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

火山引擎 最新活动