Всем доброго вечера.
Переписываю шустрый кравлер с связки [curl multi & c-ares] на
tornado.httpclient.AsyncHTTPClient.
Покопался в документации, наваял простенький скрипт
@tornado.gen.coroutine
def test():
def handle_response(response):
print 'handle %s' % response.code
num_of_try, num_of_conn = 10000, 500
tornado.httpclient.AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient", max_clients=num_of_conn)
http_client = tornado.httpclient.AsyncHTTPClient()
responses = yield [http_client.fetch("http://ya.ru/", callback=handle_response) for i in xrange(num_of_try)]
if __name__ == '__main__':
tornado.ioloop.IOLoop.current().run_sync(test)
На первый взгляд, всё работает, но возникни какая HTTPError и обработка будущего выкинет мне исключение в основном потоке, тогда как я хотел бы его видеть в обработчике. Если покопаться в коде торнады, станет видно, что это задуманное поведение (если yield обрабатывает будущее, которое выкинуло исключение, торнада его перекинет).
Долго пытался обойти такое поведение и в конечном итоге код принял следующий вид:
@tornado.gen.coroutine
def test():
def handle_response(response):
print 'handle %s' % response.code
num_of_try, num_of_conn = 10000, 500
tornado.httpclient.AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient", max_clients=num_of_conn)
http_client = tornado.httpclient.AsyncHTTPClient()
keys = set(range(num_of_try))
for i in keys:
http_client.fetch("http://ya.ru/", callback=(yield tornado.gen.Callback(i)))
while keys:
key, res = yield yieldpoints.WaitAny(keys)
handle_request(res)
keys.remove(key)
if __name__ == '__main__':
tornado.ioloop.IOLoop.current().run_sync(test)
Заместо ожидания будущего напрямую мы ждём вызова уникального колбека и сами вызываем обработчик событий.
В чём вопрос: можно ли достичь аналогичного поведения без таких жутких костылей с уникальными сетами ключей и колбеками на них?