如何给请求打上默认超时的补丁的教程

91 阅读2分钟

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秒,同时为少数已知的慢速服务设置明确的长超时。