Похоже, вы пишете что-то вроде веб-сканера. Ваша проблема вызвана тайм-аутом напрямую, но в глубине, связана с параллельным паттерном торнадо.
Конечно, AsyncHTTPClient
в торнадо может автоматически ставить запросы в очередь. На самом деле, AsyncHTTPClient
отправит 10 запросов (по умолчанию) в пакете, и заблокирует ожидание их результата, а затем отправит следующий пакет. Запросы внутри пакета не являются блочными и обрабатываются параллельно, но это блок между партиями. И обратный вызов для каждого запроса вызывается не сразу после того, как этот запрос был выполнен, а после того, как этот пакет запросов был выполнен, а затем вызвал 10 обратных вызовов.
Возвращаясь к вашей проблеме, вам не нужно использовать ioloop.PeriodicCallback
для пошагового выполнения запросов, поскольку AsyncHTTPClient
в торнадо может автоматически ставить запросы в очередь. Вы можете назначить все запросы за один раз, позвольте AsyncHTTPClient
планировать запросы.
Но возникает проблема, связанная с тем, что запросы в очереди ожидания по-прежнему потребляют время ожидания ! Потому что запросы блокируются между пакетами . Более поздние запросы здесь просто блокируются и отправляют партию за партией, а не помещают их в специальную готовую очередь и отправляют новые запросы после получения ответа.
Поэтому время ожидания по умолчанию, установленное на 20 с, слишком короткое, если запланировано много запросов. Если вы просто делаете демонстрацию, вы можете напрямую установить время ожидания на float('inf')
. Если вы делаете что-то серьезное, вы должны использовать попытку / исключение повторного цикла.
Вы можете найти, как установить тайм-аут из tornado/httpclient.py
, цитата здесь.
connect_timeout
: время ожидания для первоначального подключения в секундах
request_timeout
: время ожидания для всего запроса в секундах
В конце я пишу простую программу, которая использует AsyncHTTPClient
для извлечения тысяч страниц из ZJU Online Judgment System. Вы можете попробовать это, а затем переписать своему сканеру. В моей сети это может принести 2800 страниц за 2 минуты. Очень хорошие результаты, в 10 раз (точно соответствуют размеру параллели) быстрее, чем последовательная выборка.
#!/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()
Дополнительно:
Если у вас есть много страниц для загрузки, и вы из тех людей, которые стремятся к максимальной производительности, вы можете посмотреть Twisted
. Я пишу ту же программу с Twisted
и вставляю ее в мой Gist . Результат потрясающий: загрузите 2800 страниц за 40 секунд.