1

我有一个连接到 URL 并获取一些 json 的 API 管理器。很简单。从方法上截取:

req = Request(url)
socket.setdefaulttimeout(timeout)
resp = urlopen(req, None, timeout)
data = resp.read()
resp.close()

它在大多数情况下都能正常工作,但在随机时间间隔内,完成请求需要 5 秒。即使超时设置为 0.5 或 1.0 或其他任何值。我已经非常仔细地记录了它,所以我 100% 确定需要时间的行是数字 #3(即 resp = urlopen(req, None, timeout))。

我已经尝试了我在超时装饰器和计时器等主题上找到的所有解决方案。(列出其中一些: Python urllib2.urlopen 即使设置了超时也会无限冻结脚本如何强制 urllib2 超时?超时 urllib2 urlopen Python 2.4 中的操作,如果完成时间过长,则使用超时功能 )

但没有任何效果。我的印象是线程在 urlopen 执行某些操作时冻结,完成后它解冻,然后所有计时器和超时返回 w 超时错误。但执行时间仍然超过 5s。

我找到了这个关于 urllib2 和分块编码处理的旧邮件列表。因此,如果问题仍然存在,那么解决方案可能是基于 httplib.HTTP 而不是 httplib.HTTPConnection 编写自定义 urlopen。另一种可能的解决方案是尝试一些多线程魔法......

这两种解决方案似乎都具有侵略性。让我感到困扰的是,超时并没有一直起作用。

脚本的执行时间不超过0.5s非常重要。任何人都知道我为什么会经历冻结或者可能是一种帮助我的方法吗?

根据接受的答案更新: 我改变了方法并改用 curl 。与 unix timeout 一起,它可以按我的意愿工作。示例代码如下:

t_timeout = str(API_TIMEOUT_TIME)
c_timeout = str(CURL_TIMEOUT_TIME)
cmd = ['timeout', t_timeout, 'curl', '--max-time', c_timeout, url]
prc = Popen(cmd, stdout=PIPE, stderr=PIPE)
response = prc.communicate()

由于 curl 只接受 int 作为超时,我添加了超时。timeout 接受浮点数。

4

1 回答 1

1

查看源代码,该timeout值实际上是 Python 在从远程主机接收数据包之间等待的最长时间。

因此,如果您将超时设置为 2 秒,并且远程主机以每秒一个数据包的速率发送 60 个数据包,则永远不会发生超时,尽管整个过程仍然需要 60 秒。

由于该urlopen()函数在远程主机完成发送所有 HTTP 标头之前不会返回,因此如果它非常缓慢地发送标头,则您无能为力。

如果您需要一个总体时间限制,您可能必须使用非阻塞 I/O 实现自己的 HTTP 客户端。

于 2013-06-25T15:48:22.147 回答