
文章目录
-
- 一、连接数是对端先说的算
- 二、客户端的三层,层层都能省
- 三、还有一个隐藏层:代理
- 四、怎么用
- 五、30 秒带走版
上一篇讲 urllib3 的 Retry,有位读者问:requests.Session 和直接 requests.get 到底差在哪?当时我说「差在连接复用」,答应补一篇实测。这篇就是。
不绕圈子,先给数字:**同样向同一个对端发 50 次 GET 请求,对端 accept 到的 TCP 连接数,最差 50 条,最好 1 条。差 50 倍。**而决定这个数字的,一半在客户端,另一半在对端——这是这篇最想讲清的事。
一、连接数是对端先说的算
测试方法:起一个本地程序,在 accept 层计数(数的是 TCP 连接建立次数,不是请求数),然后用不同姿势打 50 次请求。
第一组结果就很反直觉:
| HTTP/1.0 对端 + 池化客户端 | 50 |
| HTTP/1.1 对端 + 同一个 PoolManager | 1 |
| HTTP/1.1 对端 + Session | 1 |
| HTTP/1.1 对端 + 无 Session | 50 |

注意第一行:客户端这边什么都没做错——PoolManager 是标准池化姿势,但它对面的对端是 HTTP/1.0,每次响应完就关连接。客户端把连接放回池里,下一次取出来发现是条死连接,只能重新握手。50 次请求,50 次握手,池化努力全部归零。
而第二行:同样的客户端,对端换成 HTTP/1.1 且响应带 Content-Length——50 次请求,对端只 accept 到 1 条连接。剩下 49 次全在这条连接上复用。
这就是连接复用的完整真相:**它是双边协议,不是单方设置。**对端不保活,客户端的池子就是个每次必换的储物柜。
二、客户端的三层,层层都能省
把客户端的姿势拆开,是三层,每层管一段:

第一层,对端保活。 HTTP/1.1 + Content-Length(或 chunked)才具备保活条件。HTTP/1.0、或者每次响应都带关闭连接的头部,客户端再怎么池化都白费。用 Python 自带 http.server 的同学注意:默认协议版本是 HTTP/1.0,很多人拿它当压测靶机,测出来的「客户端性能」其实是对端先关的连接——我这次就先在它上面栽了一次(见文末复盘)。
第二层,客户端池化。 一个 PoolManager(或 requests.Session)走完全部请求。反过来,每次请求新建一个 PoolManager:20 次请求就是 20 条连接——连接建了用一次就扔。requests.get() 本质上就是后者:它内部每次新建 Session。
第三层,容量配置。 PoolManager(maxsize=N) 卡并发连接上限,block=True/False 决定超额时的行为。这层的实测对照最有意思:

4 个并发慢请求(每个对端 sleep 0.25 秒)+ maxsize=2:
- block=True:只用 2 条连接,但 4 个请求全部完成要 0.53 秒——后 2 个请求在池边等了 0.27 秒,等前面的连接腾出来复用;
- block=False:建了 4 条连接,全部并行,0.26 秒完成。
一个用延迟换连接数,一个用连接数换延迟。没有对错,取决于你在哪个资源上更紧张:容器里文件描述数有限、对端有连接数限制,选 block=True;追求吞吐且对端扛得住,选 block=False。默认是 block=False——所以默认行为是「宁可多建连接」。
三、还有一个隐藏层:代理
这次实测还顺手验证了上一篇的坑 4。本机环境变量里有 http_proxy,requests.get(不设 trust_env=False)打本地程序,50 次请求 50 条连接,而且最慢——请求全被送去代理转发,代理每跳一次建一条连接,本地对端的池化对象根本不在链路上。
想复现连接池收益,两条路:urllib3.PoolManager 直连(默认不读环境变量),或者 requests.Session(trust_env=False)。后者实测 50 次请求 1 条连接,50/50 成功。
四、怎么用
import requests
s = requests.Session()
s.trust_env = False # 内网/本地调用必须关,否则走代理(1018① 坑 4)
s.headers.update({"Connection": "keep-alive"})
# 整个程序复用一个 Session;连接池默认 maxsize=10,按 host 分池
for url in urls:
r = s.get(url, timeout=(3.05, 10))
三个默认值值得知道:HTTPAdapter 默认 pool_connections=10(按 host 的池子数)、pool_maxsize=10(每个池的连接上限)、pool_block=False(超额新建)。打同一个 host 的并发超过 10 时,第 11 个起就在静默新建连接——想卡住就显式设 pool_maxsize 并把 pool_block 设 True。
五、30 秒带走版


