You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Requests Session运行6小时后持续抛出ConnectionError求助

问题:Python Requests Session运行6小时后持续报ConnectionError

环境与场景

使用Raspberry Pi 3 Model B V1.2搭配BME688传感器,持续向openSenseMap传输环境数据。代码初期运行正常,可稳定传输约6小时,之后突然持续收到ConnectionError,已排除速率限制、网络问题(启动第二个应用实例可正常连接)。

代码实现

main.py

RATE_LIMITS_TIME = 20 

def main() -> None:
    """
    Runs main process of recording and sending measurements to the OpenSenseMap.
    live sensor measurements every ten seconds due to rate limits
    """
    # create a single TCP connection
    session = requests.Session()
    
    while True:
    
        try:
            response = send_measurements_to_server(session)
        except requests.exceptions.ConnectionError:
            # renew session
            session = requests.Session()
            response = "-- CONNECTION FAILURE --"
        except requests.exceptions.Timeout:
            print("Request timed out, skipping this cycle")
            response = "-- TIMEOUT --"
         
        print(response,datetime.datetime.now())
        time.sleep(RATE_LIMITS_TIME)

send_to_server.py

def send_measurements_to_server(session: requests.Session) -> int:

    opensensemap_url = "https://api.opensensemap.org/boxes/"
    adjusted_url = opensensemap_url + SENSE_BOX_ID + "/data"

    measurement = record_measurement()

    body = {HUMIDITY_SENSOR_ID: [measurement.humidity],
            TEMPERATURE_SENSOR_ID: [measurement.temperature],
            AIR_PRESSURE_SENSOR_ID: [measurement.pressure],
            GAS_RESISTANCE_SENSOR_ID: [measurement.gas_resistance]
            }
    
    response = session.post(adjusted_url, json=body, timeout=10) 

    return response

return_measurement.py

def record_measurement() -> measurementClass:
    """Records and returns the measurement of the bme680 sensor"""

    try:
        sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)

        new_measurement = measurementClass(
                    sensor.data.temperature,
                    sensor.data.pressure,
                    sensor.data.humidity,
                    sensor.data.gas_resistance)
        
        print(sensor.data.temperature,
                    sensor.data.pressure,
                    sensor.data.humidity,
                    sensor.data.gas_resistance)
 
       
 
    except (RuntimeError, IOError):
        
        # if the sensor fails during data collection we use replacement values
        print("Sensor connection disrupted -- Using replacement values")
        
        new_measurement = create_artificial_measurement()   

    return new_measurement

关键日志

28.2 962.51 33.837 102400000.0
<Response [201]> 2025-05-14 00:38:49.823012
28.15 962.51 33.847 102400000.0
<Response [201]> 2025-05-14 00:39:09.983847
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:39:30.056937
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:39:50.175899
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:40:10.262512
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:40:30.330664
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:40:50.450122
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:41:10.604180
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:41:30.702610
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:41:50.801585
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:42:10.918886
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:42:31.072819
Sensor connection disrupted -- Using replacement values
<Response [201]> 2025-05-14 00:42:51.151268
Sensor connection disrupted -- Using replacement values
Request timed out, skipping this cycle
-- TIMEOUT -- 2025-05-14 00:43:21.174376
28.02 962.53 34.613 102400000.0
-- CONNECTION FAILURE -- 2025-05-14 00:43:41.249583
$ From here on out only Connection Failures $

已尝试的方案

  • 每次请求创建新Session
  • 全程使用单个Session
  • 仅在收到ConnectionError时生成新Session
    以上方案均无法解决问题,目前怀疑是否与树莓派的垃圾回收机制有关。

分析与解决方案

可能原因

  1. TCP连接被服务器主动关闭:服务器通常会对闲置连接设置超时策略,即使每20秒发送一次请求,若中间出现超时,Session持有的连接可能已失效,但现有逻辑未完全覆盖连接异常的清理场景。
  2. 传感器资源泄漏:每次采集数据都重新初始化BME680实例,频繁创建销毁传感器对象可能导致I2C总线资源占用异常,间接影响网络请求的底层资源分配。
  3. 连接池耗尽:Requests Session默认维护连接池,若多次异常未正确释放连接,会导致后续请求无法获取可用连接,引发ConnectionError。

修复建议

  1. 完善Session异常清理逻辑:捕获ConnectionError时,先关闭旧Session的所有连接再重建:
except requests.exceptions.ConnectionError:
    session.close()
    session = requests.Session()
    response = "-- CONNECTION FAILURE --"
  1. 优化传感器初始化逻辑:全局初始化一次传感器,避免频繁创建实例导致资源泄漏:
    修改return_measurement.py:
# 全局初始化传感器
try:
    sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
except (RuntimeError, IOError):
    sensor = None

def record_measurement() -> measurementClass:
    """Records and returns the measurement of the bme680 sensor"""
    global sensor
    try:
        if not sensor:
            sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
        sensor.get_sensor_data()  # 触发数据采集
        new_measurement = measurementClass(
                    sensor.data.temperature,
                    sensor.data.pressure,
                    sensor.data.humidity,
                    sensor.data.gas_resistance)
        
        print(sensor.data.temperature,
                    sensor.data.pressure,
                    sensor.data.humidity,
                    sensor.data.gas_resistance)
 
    except (RuntimeError, IOError):
        print("Sensor connection disrupted -- Using replacement values")
        sensor = None  # 标记失效,下次尝试重连
        new_measurement = create_artificial_measurement()   

    return new_measurement
  1. 定时主动重建Session:每隔一段时间(比如1小时)主动关闭并重建Session,避免长时间持有的连接出现不可预知问题:
    修改main.py:
import time
from datetime import datetime

RATE_LIMITS_TIME = 20 
SESSION_RENEW_INTERVAL = 3600  # 1小时

def main() -> None:
    session = requests.Session()
    last_renew_time = datetime.now()
    
    while True:
        # 定时重建Session
        if (datetime.now() - last_renew_time).total_seconds() >= SESSION_RENEW_INTERVAL:
            session.close()
            session = requests.Session()
            last_renew_time = datetime.now()
        
        try:
            response = send_measurements_to_server(session)
        except requests.exceptions.ConnectionError:
            session.close()
            session = requests.Session()
            response = "-- CONNECTION FAILURE --"
        except requests.exceptions.Timeout:
            print("Request timed out, skipping this cycle")
            response = "-- TIMEOUT --"
         
        print(response,datetime.datetime.now())
        time.sleep(RATE_LIMITS_TIME)
  1. 配置连接池与重试策略:显式设置Session的连接池参数和重试规则,提升连接稳定性:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session():
    session = requests.Session()
    # 设置重试策略
    retry = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[429, 500, 502, 503, 504]
    )
    adapter = HTTPAdapter(max_retries=retry, pool_connections=10, pool_maxsize=10)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    return session

# 在main中使用
session = create_session()

内容的提问来源于stack exchange,提问作者ölkjamin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:17:02