Русский

Глубокое исследование Tornado, веб-фреймворка и библиотеки асинхронных сетей на Python. Узнайте, как создавать масштабируемые, высокопроизводительные приложения с подробными объяснениями, примерами и лучшими практиками.

Документация Tornado: Полное руководство для разработчиков по всему миру

Tornado — это веб-фреймворк и библиотека асинхронных сетей на Python, изначально разработанная в FriendFeed. Он особенно хорошо подходит для длинных опросов (long-polling), WebSockets и других приложений, требующих долговременного соединения с каждым пользователем. Его неблокирующий сетевой ввод-вывод делает его чрезвычайно масштабируемым и мощным выбором для создания высокопроизводительных веб-приложений. Это исчерпывающее руководство проведет вас через основные концепции Tornado и предоставит практические примеры для начала работы.

Что такое Tornado?

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

Ключевые особенности Tornado:

Настройка вашей среды Tornado

Прежде чем погрузиться в разработку на Tornado, вам необходимо настроить свою среду. Вот пошаговое руководство:

  1. Установите Python: Убедитесь, что у вас установлен Python 3.6 или выше. Вы можете скачать его с официального сайта Python (python.org).
  2. Создайте виртуальную среду (рекомендуется): Используйте venv или virtualenv для создания изолированной среды для вашего проекта:
    python3 -m venv myenv
    source myenv/bin/activate  # для Linux/macOS
    myenv\Scripts\activate  # для Windows
  3. Установите Tornado: Установите Tornado с помощью pip:
    pip install tornado

Ваше первое приложение на Tornado

Давайте создадим простое приложение "Привет, мир!" на Tornado. Создайте файл с именем app.py и добавьте следующий код:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
 def get(self):
  self.write("Привет, мир!")

def make_app():
 return tornado.web.Application([
  (r"/", MainHandler),
 ])

if __name__ == "__main__":
 app = make_app()
 app.listen(8888)
 tornado.ioloop.IOLoop.current().start()

Теперь запустите приложение из вашего терминала:

python app.py

Откройте ваш веб-браузер и перейдите по адресу http://localhost:8888. Вы должны увидеть сообщение "Привет, мир!".

Объяснение:

Обработчики запросов и маршрутизация

Обработчики запросов являются основой веб-приложений Tornado. Они определяют, как обрабатывать входящие HTTP-запросы в зависимости от URL. Маршрутизация сопоставляет URL с конкретными обработчиками запросов.

Определение обработчиков запросов:

Чтобы создать обработчик запросов, унаследуйте класс tornado.web.RequestHandler и реализуйте соответствующие методы HTTP (get, post, put, delete и т.д.).

class MyHandler(tornado.web.RequestHandler):
 def get(self):
  self.write("Это GET-запрос.")

 def post(self):
  data = self.request.body.decode('utf-8')
  self.write(f"Получены POST-данные: {data}")

Маршрутизация:

Маршрутизация настраивается при создании tornado.web.Application. Вы предоставляете список кортежей, где каждый кортеж содержит шаблон URL и соответствующий обработчик запросов.

app = tornado.web.Application([
 (r"/", MainHandler),
 (r"/myhandler", MyHandler),
])

Шаблоны URL:

Шаблоны URL являются регулярными выражениями. Вы можете использовать группы регулярных выражений для захвата частей URL и передачи их в качестве аргументов в методы обработчика запросов.

class UserHandler(tornado.web.RequestHandler):
 def get(self, user_id):
  self.write(f"ID пользователя: {user_id}")

app = tornado.web.Application([
 (r"/user/([0-9]+)", UserHandler),
])

В этом примере /user/([0-9]+) соответствует URL-адресам вроде /user/123. Часть ([0-9]+) захватывает одну или несколько цифр и передает их в качестве аргумента user_id в метод get обработчика UserHandler.

Шаблонизация

Tornado включает простой и эффективный движок шаблонов. Шаблоны используются для динамической генерации HTML, отделяя логику представления от логики приложения.

Создание шаблонов:

Шаблоны обычно хранятся в отдельных файлах (например, index.html). Вот простой пример:

<!DOCTYPE html>
<html>
<head>
 <title>Мой сайт</title>
</head>
<body>
 <h1>Добро пожаловать, {{ name }}!</h1>
 <p>Сегодня {{ today }}.</p>
</body>
</html>

{{ name }} и {{ today }} — это заполнители, которые будут заменены фактическими значениями при рендеринге шаблона.

Рендеринг шаблонов:

Для рендеринга шаблона используйте метод render() в вашем обработчике запросов:

class TemplateHandler(tornado.web.RequestHandler):
 def get(self):
  name = "Иван Петров"
  today = "2023-10-27"
  self.render("index.html", name=name, today=today)

Убедитесь, что настройка template_path правильно сконфигурирована в настройках вашего приложения. По умолчанию Tornado ищет шаблоны в каталоге с именем templates в том же каталоге, что и файл вашего приложения.

app = tornado.web.Application([
 (r"/template", TemplateHandler),
], template_path="templates")

Синтаксис шаблонов:

Шаблоны Tornado поддерживают различные функции, включая:

Асинхронные операции

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

@tornado.gen.coroutine:

Декоратор @tornado.gen.coroutine позволяет писать асинхронный код с использованием ключевого слова yield. Это делает асинхронный код похожим на синхронный, улучшая читаемость и поддерживаемость.

import tornado.gen
import tornado.httpclient

class AsyncHandler(tornado.web.RequestHandler):
 @tornado.gen.coroutine
 def get(self):
  http_client = tornado.httpclient.AsyncHTTPClient()
  response = yield http_client.fetch("http://example.com")
  self.write(response.body.decode('utf-8'))

В этом примере http_client.fetch() — это асинхронная операция, которая возвращает Future. Ключевое слово yield приостанавливает выполнение сопрограммы до тех пор, пока Future не будет разрешен. Как только Future разрешается, сопрограмма возобновляет работу, и тело ответа записывается клиенту.

tornado.concurrent.Future:

Future представляет результат асинхронной операции, который может быть еще не доступен. Вы можете использовать объекты Future для объединения асинхронных операций в цепочку и обработки ошибок.

tornado.ioloop.IOLoop:

IOLoop — это сердце асинхронного движка Tornado. Он отслеживает файловые дескрипторы и сокеты на предмет событий и направляет их соответствующим обработчикам. Обычно вам не нужно напрямую взаимодействовать с IOLoop, но важно понимать его роль в обработке асинхронных операций.

WebSockets

Tornado обеспечивает отличную поддержку WebSockets, позволяя осуществлять обмен данными в реальном времени между сервером и клиентами. WebSockets идеально подходят для приложений, требующих двунаправленной связи с низкой задержкой, таких как чаты, онлайн-игры и панели мониторинга в реальном времени.

Создание обработчика WebSocket:

Чтобы создать обработчик WebSocket, унаследуйте класс tornado.websocket.WebSocketHandler и реализуйте следующие методы:

import tornado.websocket

class WebSocketHandler(tornado.websocket.WebSocketHandler):
 def open(self):
  print("WebSocket открыт")

 def on_message(self, message):
  self.write_message(f"Вы отправили: {message}")

 def on_close(self):
  print("WebSocket закрыт")

 def check_origin(self, origin):
  return True # Разрешить междоменные WebSocket-соединения

Интеграция WebSockets в ваше приложение:

Добавьте обработчик WebSocket в конфигурацию маршрутизации вашего приложения:

app = tornado.web.Application([
 (r"/ws", WebSocketHandler),
])

Реализация на стороне клиента:

На стороне клиента вы можете использовать JavaScript для установления WebSocket-соединения и отправки/получения сообщений:

const websocket = new WebSocket("ws://localhost:8888/ws");

websocket.onopen = () => {
 console.log("WebSocket-соединение установлено");
 websocket.send("Привет от клиента!");
};

websocket.onmessage = (event) => {
 console.log("Получено сообщение:", event.data);
};

websocket.onclose = () => {
 console.log("WebSocket-соединение закрыто");
};

Аутентификация и безопасность

Безопасность является критически важным аспектом разработки веб-приложений. Tornado предоставляет несколько функций для защиты ваших приложений, включая аутентификацию, авторизацию и защиту от распространенных веб-уязвимостей.

Аутентификация:

Аутентификация — это процесс проверки личности пользователя. Tornado предоставляет встроенную поддержку различных схем аутентификации, включая:

Авторизация:

Авторизация — это процесс определения, имеет ли пользователь разрешение на доступ к определенному ресурсу. Вы можете реализовать логику авторизации в ваших обработчиках запросов для ограничения доступа на основе ролей или разрешений пользователя.

Лучшие практики безопасности:

Развертывание

Развертывание приложения Tornado включает в себя несколько шагов, в том числе настройку веб-сервера, установку менеджера процессов и оптимизацию производительности.

Веб-сервер:

Вы можете развернуть Tornado за веб-сервером, таким как Nginx или Apache. Веб-сервер действует как обратный прокси, перенаправляя входящие запросы приложению Tornado.

Менеджер процессов:

Менеджер процессов, такой как Supervisor или systemd, может использоваться для управления процессом Tornado, обеспечивая его автоматический перезапуск в случае сбоя.

Оптимизация производительности:

Интернационализация (i18n) и локализация (l10n)

При создании приложений для глобальной аудитории важно учитывать интернационализацию (i18n) и локализацию (l10n). i18n — это процесс проектирования приложения таким образом, чтобы его можно было адаптировать к различным языкам и регионам без инженерных изменений. l10n — это процесс адаптации интернационализированного приложения для определенного языка или региона путем добавления специфичных для локали компонентов и перевода текста.

Tornado и i18n/l10n

Сам Tornado не имеет встроенных библиотек для i18n/l10n. Однако вы можете легко интегрировать стандартные библиотеки Python, такие как `gettext`, или более сложные фреймворки, такие как Babel, для обработки i18n/l10n в вашем приложении Tornado.

Пример с использованием `gettext`:

1. **Настройте свои локали:** Создайте каталоги для каждого языка, который вы хотите поддерживать, содержащие каталоги сообщений (обычно файлы `.mo`).

locales/
 en/LC_MESSAGES/messages.mo
 fr/LC_MESSAGES/messages.mo
 de/LC_MESSAGES/messages.mo

2. **Извлеките переводимые строки:** Используйте инструмент, такой как `xgettext`, чтобы извлечь переводимые строки из вашего кода Python в файл `.po` (Portable Object). Этот файл будет содержать исходные строки и заполнители для переводов.

xgettext -d messages -o locales/messages.po your_tornado_app.py

3. **Переведите строки:** Переведите строки в файлах `.po` для каждого языка.

4. **Скомпилируйте переводы:** Скомпилируйте файлы `.po` в файлы `.mo` (Machine Object), которые используются `gettext` во время выполнения.

msgfmt locales/fr/LC_MESSAGES/messages.po -o locales/fr/LC_MESSAGES/messages.mo

5. **Интегрируйте в ваше приложение Tornado:**

import gettext
import locale
import os
import tornado.web

class BaseHandler(tornado.web.RequestHandler):
 def initialize(self):
  try:
  locale.setlocale(locale.LC_ALL, self.get_user_locale().code)
  except locale.Error:
  # Обработка случаев, когда локаль не поддерживается системой
  print(f"Локаль {self.get_user_locale().code} не поддерживается")

  translation = gettext.translation('messages', 'locales', languages=[self.get_user_locale().code])
  translation.install()
  self._ = translation.gettext

 def get_current_user_locale(self):
  # Логика для определения локали пользователя (например, из заголовка Accept-Language, настроек пользователя и т.д.)
  # Это упрощенный пример - вам понадобится более надежное решение
  accept_language = self.request.headers.get('Accept-Language', 'en')
  return tornado.locale.get(accept_language.split(',')[0].split(';')[0])

class MainHandler(BaseHandler):
 def get(self):
  self.render("index.html", _=self._)

settings = {
 "template_path": os.path.join(os.path.dirname(__file__), "templates"),
}

app = tornado.web.Application([
 (r"/", MainHandler),
], **settings)

6. **Измените ваши шаблоны:** Используйте функцию `_()` (привязанную к `gettext.gettext`), чтобы пометить строки для перевода в ваших шаблонах.

<h1>{{ _("Добро пожаловать на наш сайт!") }}</h1>
<p>{{ _("Это переведенный абзац.") }}</p>

Важные соображения для глобальной аудитории:

Продвинутые темы

Пользовательские страницы ошибок:

Вы можете настроить страницы ошибок, которые Tornado отображает при возникновении ошибки. Это позволяет обеспечить более дружелюбный пользовательский опыт и включать отладочную информацию.

Пользовательские настройки:

Вы можете определять пользовательские настройки в конфигурации вашего приложения и получать к ним доступ в ваших обработчиках запросов. Это полезно для хранения специфичных для приложения параметров, таких как строки подключения к базе данных или API-ключи.

Тестирование:

Тщательно тестируйте ваши приложения Tornado, чтобы убедиться, что они функционируют правильно и безопасно. Используйте модульные тесты, интеграционные тесты и сквозные тесты для охвата всех аспектов вашего приложения.

Заключение

Tornado — это мощный и универсальный веб-фреймворк, который хорошо подходит для создания масштабируемых, высокопроизводительных веб-приложений. Его асинхронная архитектура, поддержка WebSocket и простой в использовании API делают его популярным выбором для разработчиков по всему миру. Следуя рекомендациям и примерам из этого исчерпывающего руководства, вы можете начать создавать свои собственные приложения на Tornado и воспользоваться его многочисленными возможностями.

Не забывайте обращаться к официальной документации Tornado для получения самой актуальной информации и лучших практик. Удачного кодирования!

Документация Tornado: Полное руководство для разработчиков по всему миру | MLOG