Найти - Пользователи
Полная версия: Tornado gen.engine и простая функция
Начало » Python для новичков » Tornado gen.engine и простая функция
1 2
alekseyxxxx
Здравствуйте. Делаю приложение на торнадо. Написал тестовый обработчик
class AdminHandler(BaseHandler):
    @tornado.web.authenticated
    @tornado.web.asynchronous
    @gen.engine
    def get(self):
        response = yield gen.Task(self.acync_func_test, 'my')
        print response
        self.render('admin/index.html')
    def acync_func_test(self, argument, callback):
        for i in xrange(1,59999000):
            i**2+2-12
        callback(argument)
Функция выполняется, но при этом блокирует все для остальных клиентов. Т.е. новые клиенты ждут, пока обработается запрос для предыдущего. Как вызвать функцию асинхронно?
lorien
Я не знаю устройства торнады, могу лишь сказать, что если у вас в асинхронной системе выполняется какой-то код и в нём нету никаких точек прерывания, типа того же yield, то этот код будет выполняться без прерываний, весь. Никаких чудес не будет, если функция async_func_test что-то делает долго, то в этом время все будут ждать её. Видимо, в функции надо генерить с помощью yield точки прерывания, наверное так и сделано в торнаде, я не знаю, как ещё по другому можно реализовать такой фунционал в python
o7412369815963
alekseyxxxx
Как вызвать функцию асинхронно?
Она и вызывается асинхронно, суть в том что она блокирующая, вам нужно использовать неблокирующие ф-ии.
Либо запустить ф-ию в другом потоке, но это плохой путь.
Лучше тяжелые обработки запускать в отдельном процессе.
alekseyxxxx
Ну вроде стало понятно.
o7412369815963
Либо запустить ф-ию в другом потоке, но это плохой путь.
Над этим тоже думал. На хабре есть пример, когда на каждый запрос создаются потоки. http://habrahabr.ru/post/116892/
class ThreadableMixin:
    def start_worker(self):
        threading.Thread(target=self.worker).start()
    def worker(self):
        try:
            self._worker()
        except tornado.web.HTTPError, e:
            self.set_status(e.status_code)
        except:
            logging.error("_worker problem", exc_info=True)
            self.set_status(500)
        tornado.ioloop.IOLoop.instance().add_callback(self.async_callback(self.results))
    def results(self):
        if self.get_status()!=200:
            self.send_error(self.get_status())
            return
        if hasattr(self, 'res'):
            self.finish(self.res)
            return
        if hasattr(self, 'redir'):
            self.redirect(self.redir)
            return
        self.send_error(500)
А потом в обработчике запускают поток.
class Handler(tornado.web.RequestHandler, ThreadableMixin):
    def _worker(self):
        self.res = self.render_string("template.html",
            title = _("Title"),
			data = self.application.db.query("select ... where object_id=%s", self.object_id)
        )
    @tornado.web.asynchronous
    def get(self, object_id):
	self.object_id = object_id
        self.start_worker(
Это хороший путь? Просто GIL же все равно выполняет только один поток. Получается, что хоть клиенты и не блокируются, но ждут ответа дольше. Или я не так понимаю?)
o7412369815963
alekseyxxxx
Получается, что хоть клиенты и не блокируются, но ждут ответа дольше. Или я не так понимаю?)
Из за GIL питон код будет работать только в одном потоке, т.е. если вы блокируете питон-кодом for in xrange то другие потоки не работают, если вы заблокируете не питон-кодом (io операции, обычно ими блокируют, db.query, urllib.urlopen, time.sleep …) то другой питон-код в других потоках будет работать.

Т.е. делать потоки имеет смысл для работы с блокирующим io. Но все же разработчики торнадо говорят про потоки как крайнюю меру (сходу ссылку не нашел, возможно где то у них на трекере).
Плюс при введении потоков общая производительность приложения падает.

Для не больших проектов (нагрузок) нормально. Для больших, я бы вынес подобный функционал в отдельный процесс, и если необходимо, общался с ним через zmq, xmlrpc и т.п. Кстати из торнадо можно асинхронно вызывать процессы, пример

А вообще, возможно вам не нужен асинхронный фреймворк для всего. Например недавний проект я сделал на bottle (wsgi) + авторизация на tornado + чат и обмен командами через websocket на gevent. - по принципу “инструмент от задачи”, а вместо tornado для авторизации рассматривал node.js ;)

Вот ещё посмотрите эту тему, я там поплакался на большой проект под tornado.
alekseyxxxx
Просто в разработке есть приложение на tornado на websocket. Все общение идет только через вебсокеты. И интересно сколько способен выдержать tornado. Как я понял, если не писать ‘асинхронным стилем’, то клиенты будут блокироваться. Ну, например, самая простая функция:

def on_message(self, msg):
    user_id = msg['id']
    user = db.query(User).get(id)
И получается, что к примеру 2000 клиентов одновременно отправят запрос, то обработка будет последовательная. Ну тут запрос небольшой, но на более сложных будет ощутимая задержка. Т.е. преимущество торнадо тут никак не реализуется. Поэтому надо использовать асинхронные функции(ну типа pymongo для работы с бд). Я правильно понял?)

P.S. Видно у вас есть опыт разработки нагруженных приложений на tornado. Не подскажите, может есть полезные ссылки и ресурсы на которых можно поподробнее почитать. Гугл весь прошарил)
o7412369815963
alekseyxxxx
И получается, что к примеру 2000 клиентов одновременно отправят запрос, то обработка будет последовательная.
Да, для mongodb можете попробовать asyncmongo, не поддерживает весь функционал, но для 99% случаев достаточно. Он асинхронный - не будет блокировать.
alekseyxxxx
И интересно сколько способен выдержать tornado
Если процесс торнадо не будет справляться, то можно запустить их несколько экземпляров (форкнуть), и балансировщиком раскидать нагрузку (пример конфига nginx), таким же образом можно задействовать несколько серверов с кучей процессов торнадо, если нагрузка большая.

При балансировке клиенты могут быть подключены к разным процессам торнадо, и если клиенты между собой должны “общаться”, нужно думать как пересылать сообщения между процессами.
o7412369815963
alekseyxxxx
Не подскажите, может есть полезные ссылки и ресурсы на которых можно поподробнее почитать.
Не знаю, я бы сначала сформулировал задачу, предполагаемые нагрузки, придумал бы несколько вариантов архитектуры, выложил на форуме для обсасывания, потом сделал бы прототип, потом если было б время, сделал бы нагрузочное тестирование.
Как то так…
alekseyxxxx
Ну сейчас на данный момент так и сделано. Т.е. нжинкс балансирует нагрузку на несколько инстансов торнадо. А клиенты с разных инстансов общаются через редис. Вариант с монго привел для примера) Основная база postgres, ну для него тоже есть асинхроная библиотека. Попробую реализовать через нее, и провести нагрузочное тестирование) Спасибо за пояснения)
plusplus
Не подскажете на эту же тему каким образом лучше реализовать такое. Пользователь заходит на сайт, нажимает кнопочку, запускается действие(настройка свитча по snmp, например), длится оно около минуты и лог этого действия realtime отображается у пользователя. Начал делать через торнадо и вебсокеты, но наткнулся на тоже самое - два пользователя одновременно не могут запустить эту настройку. Как мне сейчас лучше поступить?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB