2014-09-30 2 views
6

У меня есть приложение для флэков, которое соединяется с заданным URL-адресом внешним сервисам (с разными, но обычно длительными ответами) и ищет там некоторые вещи. После этого есть некоторые тяжелые операции процессора по извлеченным данным. Это займет некоторое время.Колба и/или торнадо - обработка трудового звонка на внешний webservice

Моя проблема: ответ от внешнего может занять некоторое время. Вы ничего не можете с этим поделать, но это становится большой проблемой, когда у вас сразу несколько запросов - запрос на флягу на внешние службы блокирует поток, а остальная часть ждет.

Очевидная трата времени и убийство приложения.

Я слышал об этой асинхронной библиотеке под названием Торнадо. И есть мои вопросы:

  1. Означает ли это, что это может справиться с обработкой нескольких запросов и просто вызвать обратный вызов сразу после ответа от внешнего?
  2. Могу ли я достичь этого с помощью своего текущего флеш-приложения (возможно, не из-за WSGI, я думаю?), Или, может быть, мне нужно переписать все приложение на Tornado?
  3. Как насчет этих тяжелых операций с процессором - это будет блокировать мой поток? В любом случае, это хорошая идея, чтобы сбалансировать нагрузку, но мне любопытно, как Торнадо справляется с этим.
  4. Возможные ловушки, gotchas?

ответ

5

Веб-сервер, встроенный в фляжку, не предназначен для использования в производстве, точно по причинам, которые вы перечисляете, - он однопоточный и легко забит, если какой-либо запрос блокирует для нетривиального количества времени. Футлярная документация lists several options for deploying it in a production environment; mod_wsgi, gunicorn, uSWGI и т. Д. Все эти варианты развертывания предоставляют механизмы для обработки параллелизма либо через потоки, процессы, либо неблокирующие операции ввода-вывода. Однако обратите внимание, что если вы выполняете операции с привязкой к процессору, единственным вариантом, который даст настоящий параллелизм, является использование нескольких процессов.

Если вы хотите использовать tornado, вам необходимо переписать заявку в стиле tornado. Поскольку его архитектура основана на явном асинхронном вводе-выводе, вы не можете использовать ее асинхронные функции, если вы развертываете его как приложение WSGI. Стиль «tornado» в основном означает использование неблокирующих API для всех операций ввода-вывода и использование подпроцессов для обработки любых длительных операций с привязкой к ЦП. Документация tornado охватывает как сделать асинхронные вызовы ввода/вывода, но вот простой пример того, как это работает:

from tornado import gen 

@gen.coroutine 
def fetch_coroutine(url): 
    http_client = AsyncHTTPClient() 
    response = yield http_client.fetch(url) 
    return response.body 

response = yield http_client.fetch(curl) вызов фактически асинхронный; он вернет управление циклу событий торнадо, когда начнется запрос, и снова возобновится после получения ответа. Это позволяет одновременно запускать несколько асинхронных HTTP-запросов, все в одном потоке.Сделайте примечание, хотя, что все, что вы делаете внутри fetch_coroutine, что не асинхронного ввода/вывода будет блокировать цикл обработки событий, и никакие другие запросы не могут быть обработаны в то время как этот код работает.

Для решения длительных операций CPU переплета, вам необходимо отправить работу на подпроцесс, чтобы избежать блокировок цикла обработки событий. Для Python это обычно означает либо multiprocessing, либо concurrent.futures. Я бы посмотрел на this question для получения дополнительной информации о том, как лучше всего интегрировать эти библиотеки с tornado. Обратите внимание, что вы не хотите поддерживать пул процессов больше, чем количество процессоров, которые у вас есть в системе, поэтому рассмотрите, сколько одновременных операций, связанных с процессором, вы планируете запускать в любой момент времени, когда вы выясняете, как масштабировать его за пределы одной машины.

Торнадо документация has a section dedicated to running behind a load balancer, а также. Они рекомендуют использовать NGINX для этой цели.

1

Торнадо кажется более подходящим для этой задачи, чем Колба. Подкласс Tornado.web.RequestHandler, выполняемый в экземпляре tornado.ioloop, должен предоставить вам обработку без блокировки запросов. Я ожидаю, что это будет выглядеть примерно так.

import tornado 
import tornado.web 
import tornado.ioloop 
import json 

class handler(tornado.web.RequestHandler): 
    def post(self): 
     self.write(json.dumps({'aaa':'bbbbb'})) 


if __name__ == '__main__': 
    app = tornado.web.Application([('/', handler)]) 
    app.listen(80, address='0.0.0.0') 
    loop = tornado.ioloop.IOLoop.instance() 
    loop.start() 

, если вы хотите, чтобы ваш пост обработчик быть асинхронными вы могли бы украсить его tornado.gen.coroutine с «AsyncHTTPClient or grequests`. Это даст вам неблокирующие запросы. вы могли бы также поместить свои вычисления в сопрограмму, хотя я не совсем уверен.

+0

Просто добавив '@ tornado.gen.coroutine' декоратора в обработчик не делает его асинхронно. Вы также должны делать неблокирующие вызовы внутри обработчика. Если вы выполняете какие-либо операции блокировки в функции, весь цикл событий будет заблокирован, если вы используете декоратор 'coroutine' или нет. – dano

+0

Ты прав, моя ошибка. можете ли вы использовать grequests для получения асинхронных запросов? – ragingSloth

+0

'grequests' использует' gevent' для обеспечения асинхронного ввода-вывода, да. Если вы используете 'tornado', вы, вероятно, захотите использовать его встроенный' AsyncHTTPClient', хотя, поскольку он интегрируется с циклом событий торнадо. – dano