Python请求模块和连接重用

49

我正在使用Python的requests模块进行HTTP通信,想知道如何重用已经建立的TCP连接?由于requests模块是无状态的,如果我多次调用相同URL的get方法,那么它每次都会创建一个新的连接吗?

谢谢!


1
http://docs.python-requests.org/en/latest/user/advanced/#keep-alive - dm03514
2个回答

119

全局函数,例如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

当您重复使用会话(因此使用会话的连接池)时,速度会更快。


5
这可能是“requests”模块可用的最佳答案,但仍不尽人意,因为它混淆了连接重用和会话处理。仅因我想共享请求之间的连接,并不意味着我想共享 cookie 等信息。最好的方式是提供一个选项,如 requests.enable_pooling(),以透明地启用按主机名和端口分组的连接池(正如被接受的答案错误地建议的那样是默认值)。 - David Ongaro
4
你说得没错,David Ongaro。然而,这是对一个特定库的特定问题的回答。回答反映了库的实现。你所建议的超出了该问题的范围。我认为更好的讨论你的建议的地方应该在请求库错误跟踪器中。 - Діма Киричук

17

requests模块不是无状态的;它允许你忽略状态并在需要时实现全局单例状态。

而且它(或者更准确地说,其中一个底层库urllib3)维护了一个由(主机名,端口)对键控的连接池,如果可以的话,它通常会自动重用连接。

正如文档中所述:

非常好的消息--多亏了urllib3,在会话内保持活动100%自动完成!在会话中发出的任何请求都会自动重复使用适当的连接!

请注意,只有在读取所有正文数据后,连接才会释放回到池中以供重用;一定要将流设置为False或读取Response对象的content属性。

那么,“如果可以”的意思是什么呢?就像上面的文档暗示的那样,如果您保持流响应对象处于活动状态,它们的连接显然无法重复使用。

此外,连接池实际上是有限的缓存,而不是无限的,因此如果您大量创建连接并且其中两个连接到同一个服务器,则不会始终重用连接,只是经常重用。但通常,这正是您想要的。

* 这里涉及到的特定状态是传输适配器。每个会话都有一个传输适配器。您可以手动指定适配器,也可以指定全局默认值,或者只使用默认全局默认值,它基本上只是包装了一个urllib3.PoolManager来管理其HTTP连接。有关更多信息,请阅读文档。


2
非常感谢您详细的回复,这真的很有帮助。我还有一个问题。在上述文档中,“session”是什么意思?我已经阅读了该文档,并且确实存在一个Session对象。我阅读了“requests”代码,并且为每个请求创建了一个Session对象。因此,如果连接仅在会话内重用,则不确定如何在两个“get”调用之间重用连接。 - gmemon
@gmemon:抱歉,那个措辞不太好。我的意思是指组成全局状态的适配器集合,特别是HTTPAdapter(它持有urllib3.PoolManager的东西)。我不知道正确的术语是什么,但“会话”显然是一个糟糕的选择。我会编辑答案。感谢您指出这一点。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接