<p>看起来你在写类似网络爬虫的东西。你的问题是直接由超时引起的,但在深层次上,与龙卷风中的平行模式有关。在</p>
<p>当然,tornado中的<code>AsyncHTTPClient</code>可以自动对请求进行排队。实际上,<code>AsyncHTTPClient</code>将成批发送10个请求(默认情况下),并阻塞以等待其结果,然后发送下一批。批内请求是非块并行处理,但批间请求是块请求。每个请求的回调不是在请求完成后立即调用的,而是在一批请求完成之后,然后调用10个回调。在</p>
<p>回到您的问题,您不需要使用<code>ioloop.PeriodicCallback</code>来递增地发出请求,因为tornado中的<code>AsyncHTTPClient</code>可以自动对请求进行排队。您可以一次性分配所有请求,让<code>AsyncHTTPClient</code>来安排请求。在</p>
<p>但问题是等待队列中的<strong>请求仍然占用超时时间</strong>!因为<strong>请求在批处理之间被阻塞</strong>。稍后的请求在这里简单地阻塞,然后一批一批地发送,而不是将它们放入一个特殊的就绪队列中,在响应到达后发送一个新的请求。在</p>
<p>因此,如果安排了许多请求,则设置为20秒的默认超时太短。如果您只是做一个演示,可以直接将超时设置为<code>float('inf')</code>。如果做了一些严重的事情,你必须使用try/except重试循环。在</p>
<p>您可以在这里找到如何从<a href="https://github.com/tornadoweb/tornado/blob/master/tornado/httpclient.py" rel="nofollow">^{<cd7>}</a>设置超时。在</p>
<blockquote>
<p><code>connect_timeout</code>: Timeout for initial connection in seconds<br/>
<code>request_timeout</code>: Timeout for entire request in seconds</p>
</blockquote>
<p>最后,我编写了一个简单的程序,使用<code>AsyncHTTPClient</code>从ZJU在线判决系统中获取数千页。你可以试试这个,然后重写到你的爬虫。在我的网络上,它可以在2分钟内获取2800页。非常好的结果,比串行获取快10倍(完全匹配并行大小)。在</p>
<pre class="lang-py prettyprint-override"><code>#!/usr/bin/env python
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.ioloop import IOLoop
baseUrl = 'http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode='
start = 1001
end = 3800
count = end - start
done = 0
client = AsyncHTTPClient()
def onResponse(response):
if response.error:
print('Error: %s' % response.error)
else:
global done
done += 1
#It is comment out here, you could uncomment it and watch something interest, that len(client.queue) is reduce 10 by 10.
#print('Queue length %s, Active client count %s, Max active clients limit %s' % (len(client.queue), len(client.active), client.max_clients))
print('Received %s, Content length %s, Done %s' % (response.effective_url[-4:], len(response.body), done))
if(done == count):
IOLoop.instance().stop()
for i in range (start, end):
request = HTTPRequest(baseUrl + str(i), connect_timeout=float('inf'), request_timeout=float('inf'))
client.fetch(request, onResponse)
print('Generated %s' % i)
IOLoop.instance().start()
</code></pre>
<h3>额外:</h3>
<p>如果您有大量的页面需要获取,并且您是那种追求最佳性能的人,那么您可以查看一下<code>Twisted</code>。我用<code>Twisted</code>编写了一个相同的程序,并将其粘贴到我的<a href="https://gist.github.com/Preffer/4bdef9a3eaf640ae429f" rel="nofollow">Gist</a>上。它的结果是非常棒的:在40秒内就可以获取2800页。在</p>