Python的requests 软件包非常流行。即使你不直接使用它,你的某个依赖项也很可能使用它。
requests设计中的一个问题是它没有默认超时。 这意味着如果远程服务器不响应,请求就会永远挂起,除非作者记得添加一个超时。问题#3070追踪了关于添加这种默认超时的讨论,但它已经开放了几年。httpx从中吸取了教训,它的默认超时为5秒。
这个 "缺失的默认值 "在我的客户ev.energy造成了一些生产事故。 远程服务器的中断导致后台任务需要45分钟,而不是4.5秒,等待不会出现的响应。 这导致其他重要的后台工作被延迟,并产生了连锁反应。
审计代码库以添加缺失的timeout 参数只能到此为止。新的第三方和第一方代码 "漏网 "了,没有超时就被部署了。
我想出了一个解决方案,将请求改为使用默认超时。 我通过patchy 来实现,它是我在运行时修补函数源代码的软件包。 Patchy 提供了一个替代猴子修补的方法,有几个优点:它可以修改函数正文中的行,如果目标函数改变,它就会失败,而且对函数的引用不需要更新。
下面是修补函数,请随意将其复制到你的项目中去
import patchy
from requests.adapters import HTTPAdapter
def patch_requests_default_timeout() -> None:
"""
Set a default timeout for all requests made with “requests”.
Upstream is waiting on this longstanding issue:
https://github.com/psf/requests/issues/3070
"""
patchy.patch(
HTTPAdapter.send,
"""\
@@ -14,6 +14,8 @@
:param proxies: (optional) The proxies dictionary to apply to the request.
:rtype: requests.Response
\"""
+ if timeout is None:
+ timeout = 5.0
try:
conn = self.get_connection(request.url, proxies)
""",
)
你需要在你的项目初始化时调用一次。 在Django项目中,这可以在一个 AppConfig:
from django.apps import AppConfig
from example.core import backports
class ExampleConfig(AppConfig):
name = "example"
def ready(self) -> None:
backports.requests.patch_default_timeout()
上面的片段使用了5秒的超时,这是从httpx复制过来的。 这对于现有的集成了几个服务的项目来说可能有点低。 你可能希望从高处开始,然后向下迭代。
在ev.energy,我使用了一个较高的值30秒,因为生产指标显示一些第三方服务需要花费一些时间来响应。 我正计划将默认超时减少到5秒,同时为少数已知的慢速服务设置明确的长超时。