Български

Задълбочен преглед на Tornado, уеб фреймуърк и асинхронна мрежова библиотека на Python. Научете как да създавате мащабируеми приложения с висока производителност с подробни обяснения, примери и най-добри практики.

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

Tornado е уеб фреймуърк на Python и асинхронна мрежова библиотека, първоначално разработена от FriendFeed. Той е особено подходящ за long-polling, WebSockets и други приложения, които изискват дълготрайна връзка с всеки потребител. Неговият неблокиращ мрежов I/O го прави изключително мащабируем и мощен избор за изграждане на уеб приложения с висока производителност. Това цялостно ръководство ще ви преведе през основните концепции на Tornado и ще предостави практически примери, за да започнете.

Какво е Tornado?

В основата си Tornado е уеб фреймуърк и асинхронна мрежова библиотека. За разлика от традиционните синхронни уеб фреймуъркове, Tornado използва еднонишкова архитектура, базирана на цикъл на събитията (event-loop). Това означава, че може да обработва много едновременни връзки, без да изисква нишка за всяка връзка, което го прави по-ефективен и мащабируем.

Ключови характеристики на 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("This is a GET request.")

 def post(self):
  data = self.request.body.decode('utf-8')
  self.write(f"Received POST data: {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"User ID: {user_id}")

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

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

Шаблони (Templating)

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

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

Шаблоните обикновено се съхраняват в отделни файлове (напр. index.html). Ето един прост пример:

<!DOCTYPE html>
<html>
<head>
 <title>My Website</title>
</head>
<body>
 <h1>Welcome, {{ name }}!</h1>
 <p>Today is {{ today }}.</p>
</body>
</html>

{{ name }} и {{ today }} са контейнери (placeholders), които ще бъдат заменени с реални стойности, когато шаблонът се рендира.

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

За да рендирате шаблон, използвайте метода 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 се крие в неговите асинхронни възможности. Асинхронните операции позволяват на вашето приложение да извършва неблокиращ I/O, подобрявайки производителността и мащабируемостта. Това е особено полезно за задачи, които включват изчакване на външни ресурси, като заявки към база данни или мрежови заявки.

@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 opened")

 def on_message(self, message):
  self.write_message(f"You sent: {message}")

 def on_close(self):
  print("WebSocket closed")

 def check_origin(self, origin):
  return True # Enable cross-origin WebSocket connections

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

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

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

Имплементация от страна на клиента:

От страна на клиента можете да използвате JavaScript, за да установите WebSocket връзка и да изпращате/получавате съобщения:

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

websocket.onopen = () => {
 console.log("WebSocket connection established");
 websocket.send("Hello from the client!");
};

websocket.onmessage = (event) => {
 console.log("Received message:", event.data);
};

websocket.onclose = () => {
 console.log("WebSocket connection closed");
};

Удостоверяване и сигурност

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

Удостоверяване (Authentication):

Удостоверяването е процес на проверка на самоличността на потребителя. Tornado предоставя вградена поддръжка за различни схеми за удостоверяване, включително:

Оторизация (Authorization):

Оторизацията е процес на определяне дали даден потребител има разрешение за достъп до определен ресурс. Можете да имплементирате логика за оторизация във вашите обработчици на заявки, за да ограничите достъпа въз основа на потребителски роли или разрешения.

Най-добри практики за сигурност:

Внедряване (Deployment)

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

Уеб сървър:

Можете да внедрите Tornado зад уеб сървър като Nginx или Apache. Уеб сървърът действа като обратно прокси (reverse proxy), препращайки входящите заявки към приложението на 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:
  # Handle cases where the locale is not supported by the system
  print(f"Locale {self.get_user_locale().code} not supported")

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

 def get_current_user_locale(self):
  # Logic to determine user's locale (e.g., from Accept-Language header, user settings, etc.)
  # This is a simplified example - you'll need a more robust solution
  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 за най-актуалната информация и най-добрите практики. Приятно кодиране!