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

Python中ThreadPool共享requests.Session时的连接回收问题

单个requests.Session在ThreadPool线程中连接回收的机制解析

先结合你的场景和观察来拆解问题:你用一个全局的requests.Session对象,在multiprocessing.dummy.ThreadPool(本质是基于线程的池)里批量发起请求,发现线程数多的时候连接会被回收,线程数少的时候不会,而且Session的ID全程不变,禁用GC也没影响。这背后的核心逻辑和requests底层依赖的urllib3连接池机制直接相关。

先搞清楚Session和连接池的基础逻辑

requests.Session本质是维护了一个urllib3的连接池集合,针对每个不同的(主机、端口、协议)组合,都会有一个独立的连接池。这个连接池的核心参数是pool_maxsize(默认值是10),它决定了针对目标服务器,最多能保持多少个长连接处于复用状态。

连接池的工作流程是这样的:

  • 当线程通过Session发起请求时,会先从对应服务器的连接池里尝试获取空闲连接;
  • 如果池里有空闲连接,直接复用;
  • 如果池里没有空闲,但当前池内的连接数还没到pool_maxsize,就新建一个连接并加入池;
  • 如果池内连接数已经达到pool_maxsize,默认会阻塞等待直到有连接被归还(由其他线程用完放回池);如果等待超时,或者设置了block=False,就会创建临时连接,用完之后直接关闭,不会放回池——这就是你看到的“连接回收”。

对应你的观察点逐一解释

  1. Session对象ID始终相同:
    你是在主线程创建的Session,线程池里的所有子线程都是共享这个全局实例的,所以id(s)全程不变是完全正常的,所有请求都复用同一个Session的连接池。

  2. 高线程数(20)时连接回收,低线程数(5)时不回收:
    这刚好对应了默认pool_maxsize=10的限制:

    • 当线程池大小是5(≤10)时,所有线程的请求都能从连接池获取或创建不超过10个的连接,用完后都能放回池里复用,所以连接不会被关闭;
    • 当线程池大小是20(>10)时,同时有20个线程在发起请求,连接池最多只能容纳10个长连接,剩下的10个请求不得不创建临时连接,这些临时连接用完后因为池已经满了,无法放回,只能被主动关闭,也就是你观察到的“回收”。
  3. 禁用GC后连接仍会回收:
    连接的关闭是urllib3的主动行为,和Python的垃圾回收完全无关——这些临时连接是因为无法进入连接池,被框架直接关闭释放,不是因为GC回收了对象。

实现“连接保持存活直至最大请求数”的方案

如果你想让所有连接都保持存活复用,只需要把连接池的pool_maxsize调整为至少等于你的线程池大小即可。可以通过HTTPAdapter来修改这个参数:

from requests.adapters import HTTPAdapter

if __name__ == "__main__":
    s = requests.Session()
    # 挂载适配器,设置连接池参数:pool_maxsize和线程池大小一致(20)
    s.mount('https://', HTTPAdapter(pool_connections=20, pool_maxsize=20))
    s.headers.update({'Accept': 'application/json'})
    main()

这样调整后,所有线程的请求都能获取到连接池里的连接,用完后放回池复用,不会再出现连接被回收的情况。

另外补充一点:如果你希望连接在空闲一段时间后再关闭,可以调整相关超时参数,但核心需求的解决关键还是匹配pool_maxsize和线程池大小。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:24:26