Python requests模块和连接重用

50 投票
2 回答
53793 浏览
提问于 2025-04-18 14:09

我正在使用Python的requests模块进行HTTP通信,我想知道怎么才能重用已经建立的TCP连接?requests模块是无状态的,如果我对同一个网址反复调用get方法,难道每次都会创建一个新的连接吗?

谢谢!!

2 个回答

120

requests.getrequests.post 这样的全局函数,每次调用时都会创建一个新的 requests.Session 实例。用这些函数建立的连接不能重复使用,因为你无法访问自动创建的会话,也不能利用它的连接池来处理后续的请求。如果你只需要发几个请求,这样用是没问题的。但如果请求比较多,你就需要自己管理会话了。

下面是使用全局 get 函数和会话时 requests 的行为简要展示。

准备工作,这部分跟问题没太大关系:

>>> import logging, requests, timeit
>>> logging.basicConfig(level=logging.DEBUG, format="%(message)s")

你会发现,每次调用 get 时,都会建立一个新的连接:

>>> _ = requests.get("https://www.wikipedia.org")
Starting new HTTPS connection (1): www.wikipedia.org
>>> _ = requests.get("https://www.wikipedia.org")
Starting new HTTPS connection (1): www.wikipedia.org

但是如果你在后续调用中使用同一个会话,连接就会被重复使用:

>>> session = requests.Session()
>>> _ = session.get("https://www.wikipedia.org")
Starting new HTTPS connection (1): www.wikipedia.org
>>> _ = session.get("https://www.wikipedia.org")
>>> _ = session.get("https://www.wikipedia.org")
>>> _ = session.get("https://www.wikipedia.org")

性能:

>>> timeit.timeit('_ = requests.get("https://www.wikipedia.org")', 'import requests', number=100)
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
...
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
52.74904417991638
>>> timeit.timeit('_ = session.get("https://www.wikipedia.org")', 'import requests; session = requests.Session()', number=100)
Starting new HTTPS connection (1): www.wikipedia.org
15.770191192626953

当你重复使用会话(也就是会话的连接池)时,速度会快很多。

16

requests模块是无状态的,如果我多次对同一个网址调用get,难道每次都会创建一个新的连接吗?

requests模块并不是无状态的;它只是让你可以忽略状态,如果你愿意的话,可以有效地使用一个全局的单例状态。

而且它(或者说它底层的一个库urllib3)会维护一个连接池,这个连接池是根据(主机名,端口)来管理的,所以通常情况下,它会“神奇地”重用一个连接,只要可以的话。

正如文档所说:

好消息——多亏了urllib3,保持连接是100%自动的,只要在一个会话中!你在会话中发出的任何请求都会自动重用合适的连接!

注意,只有在所有的主体数据都被读取后,连接才会被释放回连接池以供重用;确保要么将stream设置为False,要么读取Response对象的content属性。

那么,“如果可以”是什么意思呢?正如上面的文档所暗示的,如果你保持流式响应对象的连接活着,那么这些连接显然是不能被重用的。

另外,连接池实际上是一个有限的缓存,而不是无限的,所以如果你频繁地创建很多连接,而其中两个连接是指向同一个服务器的,你并不总是能重用那个连接,只是“经常”能重用。但通常情况下,这正是你想要的。


* 这里相关的特定状态是传输适配器。每个会话都有一个传输适配器。你可以手动指定适配器,或者指定一个全局默认适配器,或者你可以直接使用默认的全局适配器,它基本上只是包装了一个urllib3.PoolManager来管理它的HTTP连接。想了解更多信息,可以查看文档。

撰写回答