Star

Tornado: пример веб приложения

Tornado - асинхронный веб фреймворк для python’а. Вначале я приведу краткий перечень плюсов и минусов tornado, а потом расскажу о типовом веб проекте с использованием этого инструмента.

Плюсы tornado

В плюсах и минусах я буду приводить свое личное ощущение по сравнению с django.

1. Асинхронность.

Торнадо представляет из себя бесконечный цикл (ioloop), который постоянно проверяет наличие событий. Все это происходит в одном потоке. К примеру кто-то обратился по адресу /home/. Допустим в качестве обработчика этого события зарегистрирован HomeHandler (handler в торнадо примерно тоже самое, что view в django). ioloop вызывает HomeHandler и начинается выполняться его код. Что происходит в это время с ioloop? Он блокируется. Eсли обратиться другой пользователь, он будет ждать, пока обработается предыдущее событие.

В чем смысл тогда?

А в том, что есть специальный механизм callback’ов. Для выполнения долгих действий мы “говорим”: выполни эту операцию (например, обращение к БД или внешний http запрос), а когда закончишь, вызови вот эту функцию. И тогда, после регистрации callback’a ioloop продолжает работать, обрабатывая другие события. В какой-то момент наша долгая задача выполниться и вызовет наш зарегистированный callback. Однако, стоит иметь в виду, функция сама по себе должна поддерживать асинхронность, если сказать “выполни time.sleep(10) и вызови этот callback”, то ioloop все равно будет заблокирована из-за time.sleep. Поэтому для асинхронных задач используются специальные функции, библиотеки.

Но все же, какое преимущество дает только один запущенный поток? Чем плохо на каждый запрос создать новый процесс или поток?

А в том, что создание потока, а тем более процесса - довольно дорогостоящая операция с точки зрения ресурсов компьютера. Представим, что на каждый запрос мы создаем новый поток. Тогда если обратяться одновременно 1000 пользователей, то мы получим 1000 потоков. Серверу придется очень тяжело, скорее всего он не справится. Конечно, мы всегда ограничиваем максимальное количество потоков и процессов, тогда следующий пользователь будет ждать, когда освободиться какой-либо из потоков.

Представим проблему посложнее - создать онлайн чат. Когда кто-то пишет в чате, все должны об этом узнать. Какие варианты решения будут на django? Например, опрашивать каждым участником по ajax о наличии нового сообщения скажем раз в 5 секунд. Такое решение быстро исчерпает все ресурсы сервера при увеличении количества участников. На каждый ajax запрос нужно создавать новое соединение и новый поток и делать это постоянно, а этого дорого. Есть вариант с keep-alive, когда мы постоянно держим соединение открытым, не обрываем его. Но это прямая дорога к C10K проблеме. Т.е. мы же не сможем держать запущенными 10000 потоков.

Вот тут на помощь приходят асинхронные решения. С ними можно использовать WebSocket’ы, или те же keep-alive http запросы, в асинхронных фреймворках они не съедят все ресурсы.

2. Работа с WebSocket’ами

Это отчасти следствие асинхронности, но лучше выделить это отдельным пунктом. Про вебсокеты можно почитать тут.

3. Менее слабая зависимость от ORM и html-шаблонизатора.

For example django’s built-in ORM works only with SQL databases. If you want to connect to mongodb, you’ll К примеру django’вский встроенный ORM работает только с SQL базами данных. Если вы хотите подключить mongodb, то вам придется отказаться (либо допиливать самому) от всех готовых приложений, которые завязаны на ORM django, включая админку. Это же вас ждет, если вы хотите использовать например SQL Alchemy. Так же, если вы хотите сменить встроенный шаблонизатор на другой (Jinja2), то опять-таки, многие готовые аппы для django могут так просто не заработать. С админкой та же беда. У tornado этих проблем меньше. Но в то же время для tornado вообще меньше готовых приложений, встроенной админки тоже, как вы понимаете, нет. И да, нужно использовать асинхронный драйвер для работы с выбранной БД, чтобы использовать все преимущества асинхронности. Не для всех БД такие драйвера есть. Точно есть для mongodb, posgresql. Для mysql - не знаю.

Минусы tornado

1. Меньшая популярность (чем у django).

Это означает, что многое из того, что есть готового для django, для tornado придется делать самому. Админку например.

2. Сложность кода.

Как ни крути, но асинхронный код писать сложнее, чем синхронный. Т.е. более высокий порог вхождения.

Проект типового приложения на tornado

Здесь https://github.com/st4lk/acl_webapp.

Преставляет из себя ACL приложение, т.е. приложение с правами доступа. Права основаны на модели: у каждого юзера есть поле permissions:

permissions = {
    'model_name_1': ['read',],
    'model_name_2': ['read', 'write'],
    'model_name_3': ['read', 'write', 'delete'],
}

В данном случае у пользователя есть права “только чтение” для модели model_name_1, “читать и записывать” для model_name_2, и “читать, записывать, удалять” для model_name_3.

Проект следует структуре django: есть приложения (apps), каждое из которых выполняет определеную функцию. Вот пример приложений:

accounts
news
pages

и т.д.

Каждое приложение содержит модели, хендлеры, формы.

Базовые хендлеры:

  • ListHandler
  • DetailHandler
  • CreateHandler
  • DeleteHandler
  • и т.д.

Все настройки определены в settings.py.

Использумые технологии

  • mongodb (база данных)
  • motor (асинхронный драйвер для БД)
  • schematics (построение абстрактных моделей БД)
  • WTForms (формы)
  • Jinja2 (html шаблоны)

P.S. Возможно, я где-то ошибся в понимании tornado. Буду рад, если вы сообщите об этом или о чем-то другом в комментариях!