Українська

Поглиблене дослідження 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  # On Linux/macOS
    myenv\Scripts\activate  # On Windows
  3. Встановіть Tornado: Встановіть Tornado за допомогою pip:
    pip install tornado

Ваш перший додаток на Tornado

Створімо простий додаток "Hello, World!" на Tornado. Створіть файл з назвою app.py і додайте наступний код:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
 def get(self):
  self.write("Hello, World!")

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. Ви повинні побачити повідомлення "Hello, World!".

Пояснення:

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

Обробники запитів є основою веб-додатків на 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 = "John Doe"
  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 для отримання найактуальнішої інформації та найкращих практик. Щасливого кодування!