Dogłębna analiza Tornado, frameworka webowego i biblioteki do programowania asynchronicznego w Pythonie. Naucz się tworzyć skalowalne, wydajne aplikacje.
Dokumentacja Tornado: Kompleksowy przewodnik dla programistów na całym świecie
Tornado to framework webowy i biblioteka do programowania asynchronicznego w Pythonie, pierwotnie opracowana w FriendFeed. Jest szczególnie dobrze przystosowana do long-polling, WebSockets i innych aplikacji wymagających długotrwałego połączenia z każdym użytkownikiem. Jej nieblokujące wejście/wyjście sieciowe czyni ją niezwykle skalowalną i potężnym narzędziem do tworzenia wysokowydajnych aplikacji internetowych. Ten kompleksowy przewodnik przeprowadzi Cię przez podstawowe koncepcje Tornado i dostarczy praktycznych przykładów, abyś mógł zacząć.
Czym jest Tornado?
W swej istocie Tornado to framework webowy i biblioteka do programowania asynchronicznego. W przeciwieństwie do tradycyjnych synchronicznych frameworków webowych, Tornado używa jednowątkowej architektury opartej na pętli zdarzeń. Oznacza to, że może obsługiwać wiele jednoczesnych połączeń bez konieczności tworzenia wątku na połączenie, co czyni go bardziej wydajnym i skalowalnym.
Kluczowe cechy Tornado:
- Asynchroniczne operacje sieciowe: Rdzeń Tornado jest zbudowany wokół asynchronicznego I/O, co pozwala na wydajną obsługę tysięcy jednoczesnych połączeń.
- Framework webowy: Zawiera funkcje takie jak request handlery, routing, szablony i uwierzytelnianie, co czyni go kompletnym frameworkiem webowym.
- Wsparcie dla WebSockets: Tornado zapewnia doskonałe wsparcie dla WebSockets, umożliwiając komunikację w czasie rzeczywistym między serwerem a klientami.
- Lekkość i szybkość: Zaprojektowany z myślą o wydajności, Tornado jest lekki i efektywny, minimalizując narzut i maksymalizując przepustowość.
- Łatwość użycia: Pomimo zaawansowanych funkcji, Tornado jest stosunkowo łatwy do nauczenia i użycia, z przejrzystym i dobrze udokumentowanym API.
Konfiguracja środowiska Tornado
Zanim zagłębisz się w rozwój z Tornado, musisz skonfigurować swoje środowisko. Oto przewodnik krok po kroku:
- Zainstaluj Pythona: Upewnij się, że masz zainstalowanego Pythona w wersji 3.6 lub nowszej. Możesz go pobrać z oficjalnej strony Pythona (python.org).
- Utwórz środowisko wirtualne (zalecane): Użyj
venv
lubvirtualenv
, aby utworzyć izolowane środowisko dla swojego projektu:python3 -m venv myenv source myenv/bin/activate # Na Linux/macOS myenv\Scripts\activate # Na Windows
- Zainstaluj Tornado: Zainstaluj Tornado za pomocą pip:
pip install tornado
Twoja pierwsza aplikacja Tornado
Stwórzmy prostą aplikację "Hello, World!" z Tornado. Utwórz plik o nazwie app.py
i dodaj następujący kod:
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()
Teraz uruchom aplikację z terminala:
python app.py
Otwórz przeglądarkę internetową i przejdź do http://localhost:8888
. Powinieneś zobaczyć komunikat "Hello, World!".
Wyjaśnienie:
tornado.ioloop
: Główna pętla zdarzeń, która obsługuje operacje asynchroniczne.tornado.web
: Dostarcza komponenty frameworka webowego, takie jak request handlery i routing.MainHandler
: Request handler, który definiuje, jak obsługiwać przychodzące żądania HTTP. Metodaget()
jest wywoływana dla żądań GET.tornado.web.Application
: Tworzy aplikację Tornado, mapując wzorce URL na request handlery.app.listen(8888)
: Uruchamia serwer, nasłuchując na porcie 8888 na przychodzące połączenia.tornado.ioloop.IOLoop.current().start()
: Uruchamia pętlę zdarzeń, która przetwarza przychodzące żądania i obsługuje operacje asynchroniczne.
Request Handlery i Routing
Request handlery są podstawą aplikacji webowych Tornado. Definiują one, jak obsługiwać przychodzące żądania HTTP na podstawie adresu URL. Routing mapuje adresy URL na określone request handlery.
Definiowanie Request Handlerów:
Aby utworzyć request handler, dziedzicz po tornado.web.RequestHandler
i zaimplementuj odpowiednie metody HTTP (get
, post
, put
, delete
, itp.).
class MyHandler(tornado.web.RequestHandler):
def get(self):
self.write("To jest żądanie GET.")
def post(self):
data = self.request.body.decode('utf-8')
self.write(f"Otrzymano dane POST: {data}")
Routing:
Routing jest konfigurowany podczas tworzenia tornado.web.Application
. Podajesz listę krotek, gdzie każda krotka zawiera wzorzec URL i odpowiadający mu request handler.
app = tornado.web.Application([
(r"/", MainHandler),
(r"/myhandler", MyHandler),
])
Wzorce URL:
Wzorce URL to wyrażenia regularne. Możesz używać grup wyrażeń regularnych do przechwytywania części adresu URL i przekazywania ich jako argumenty do metod request handlera.
class UserHandler(tornado.web.RequestHandler):
def get(self, user_id):
self.write(f"ID użytkownika: {user_id}")
app = tornado.web.Application([
(r"/user/([0-9]+)", UserHandler),
])
W tym przykładzie /user/([0-9]+)
pasuje do adresów URL takich jak /user/123
. Część ([0-9]+)
przechwytuje jedną lub więcej cyfr i przekazuje je jako argument user_id
do metody get
handlera UserHandler
.
Szablony
Tornado zawiera prosty i wydajny silnik szablonów. Szablony są używane do dynamicznego generowania HTML, oddzielając logikę prezentacji od logiki aplikacji.
Tworzenie szablonów:
Szablony są zazwyczaj przechowywane w osobnych plikach (np. index.html
). Oto prosty przykład:
<!DOCTYPE html>
<html>
<head>
<title>Moja strona internetowa</title>
</head>
<body>
<h1>Witaj, {{ name }}!</h1>
<p>Dzisiaj jest {{ today }}.</p>
</body>
</html>
{{ name }}
oraz {{ today }}
to symbole zastępcze, które zostaną zastąpione rzeczywistymi wartościami podczas renderowania szablonu.
Renderowanie szablonów:
Aby wyrenderować szablon, użyj metody render()
w swoim request handlerze:
class TemplateHandler(tornado.web.RequestHandler):
def get(self):
name = "Jan Kowalski"
today = "2023-10-27"
self.render("index.html", name=name, today=today)
Upewnij się, że ustawienie template_path
jest poprawnie skonfigurowane w ustawieniach aplikacji. Domyślnie Tornado szuka szablonów w katalogu o nazwie templates
w tym samym katalogu, co plik aplikacji.
app = tornado.web.Application([
(r"/template", TemplateHandler),
], template_path="templates")
Składnia szablonów:
Szablony Tornado obsługują różne funkcje, w tym:
- Zmienne:
{{ variable }}
- Sterowanie przepływem:
{% if condition %} ... {% else %} ... {% end %}
,{% for item in items %} ... {% end %}
- Funkcje:
{{ function(argument) }}
- Dołączanie:
{% include "another_template.html" %}
- Ucieczka znaków (Escaping): Tornado automatycznie ucieka encje HTML, aby zapobiec atakom cross-site scripting (XSS). Możesz wyłączyć ucieczkę znaków za pomocą
{% raw variable %}
.
Operacje asynchroniczne
Siła Tornado leży w jego możliwościach asynchronicznych. Operacje asynchroniczne pozwalają Twojej aplikacji na wykonywanie nieblokującego I/O, co poprawia wydajność i skalowalność. Jest to szczególnie przydatne w zadaniach, które wymagają oczekiwania na zasoby zewnętrzne, takie jak zapytania do bazy danych czy żądania sieciowe.
@tornado.gen.coroutine
:
Dekorator @tornado.gen.coroutine
pozwala pisać kod asynchroniczny z użyciem słowa kluczowego yield
. Dzięki temu kod asynchroniczny wygląda i zachowuje się bardziej jak kod synchroniczny, co poprawia czytelność i łatwość konserwacji.
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'))
W tym przykładzie http_client.fetch()
jest operacją asynchroniczną, która zwraca obiekt Future
. Słowo kluczowe yield
zawiesza wykonanie korutyny do czasu rozwiązania obiektu Future
. Gdy Future
zostanie rozwiązany, korutyna wznawia działanie, a treść odpowiedzi jest zapisywana do klienta.
tornado.concurrent.Future
:
Future
reprezentuje wynik operacji asynchronicznej, która może jeszcze nie być dostępna. Możesz używać obiektów Future
do łączenia operacji asynchronicznych i obsługi błędów.
tornado.ioloop.IOLoop
:
IOLoop
to serce asynchronicznego silnika Tornado. Monitoruje deskryptory plików i gniazda w poszukiwaniu zdarzeń i przekazuje je do odpowiednich handlerów. Zazwyczaj nie musisz bezpośrednio wchodzić w interakcje z IOLoop
, ale ważne jest, aby rozumieć jego rolę w obsłudze operacji asynchronicznych.
WebSockets
Tornado zapewnia doskonałe wsparcie dla WebSockets, umożliwiając komunikację w czasie rzeczywistym między serwerem a klientami. WebSockets są idealne dla aplikacji wymagających dwukierunkowej komunikacji o niskim opóźnieniu, takich jak aplikacje czatowe, gry online i pulpity nawigacyjne w czasie rzeczywistym.
Tworzenie Handlera WebSocket:
Aby utworzyć handler WebSocket, dziedzicz po tornado.websocket.WebSocketHandler
i zaimplementuj następujące metody:
open()
: Wywoływana, gdy nawiązywane jest nowe połączenie WebSocket.on_message(message)
: Wywoływana, gdy otrzymana jest wiadomość od klienta.on_close()
: Wywoływana, gdy połączenie WebSocket jest zamykane.
import tornado.websocket
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print("WebSocket otwarty")
def on_message(self, message):
self.write_message(f"Wysłałeś: {message}")
def on_close(self):
print("WebSocket zamknięty")
def check_origin(self, origin):
return True # Włącz połączenia WebSocket z innych domen
Integracja WebSockets z Twoją aplikacją:
Dodaj handler WebSocket do konfiguracji routingu swojej aplikacji:
app = tornado.web.Application([
(r"/ws", WebSocketHandler),
])
Implementacja po stronie klienta:
Po stronie klienta możesz użyć JavaScriptu, aby nawiązać połączenie WebSocket i wysyłać/odbierać wiadomości:
const websocket = new WebSocket("ws://localhost:8888/ws");
websocket.onopen = () => {
console.log("Połączenie WebSocket nawiązane");
websocket.send("Witaj od klienta!");
};
websocket.onmessage = (event) => {
console.log("Otrzymano wiadomość:", event.data);
};
websocket.onclose = () => {
console.log("Połączenie WebSocket zamknięte");
};
Uwierzytelnianie i bezpieczeństwo
Bezpieczeństwo jest kluczowym aspektem tworzenia aplikacji internetowych. Tornado oferuje kilka funkcji, które pomogą Ci zabezpieczyć aplikacje, w tym uwierzytelnianie, autoryzację i ochronę przed powszechnymi podatnościami internetowymi.
Uwierzytelnianie:
Uwierzytelnianie to proces weryfikacji tożsamości użytkownika. Tornado zapewnia wbudowane wsparcie dla różnych schematów uwierzytelniania, w tym:
- Uwierzytelnianie oparte na ciasteczkach: Przechowuj dane uwierzytelniające użytkownika w ciasteczkach.
- Uwierzytelnianie stron trzecich (OAuth): Zintegruj z popularnymi platformami społecznościowymi, takimi jak Google, Facebook i Twitter.
- Klucze API: Używaj kluczy API do uwierzytelniania żądań API.
Autoryzacja:
Autoryzacja to proces określania, czy użytkownik ma uprawnienia do dostępu do określonego zasobu. Możesz zaimplementować logikę autoryzacji w swoich request handlerach, aby ograniczyć dostęp na podstawie ról lub uprawnień użytkownika.
Najlepsze praktyki bezpieczeństwa:
- Ochrona przed Cross-Site Scripting (XSS): Tornado automatycznie ucieka encje HTML, aby zapobiec atakom XSS. Zawsze używaj metody
render()
do renderowania szablonów i unikaj generowania HTML bezpośrednio w swoich request handlerach. - Ochrona przed Cross-Site Request Forgery (CSRF): Włącz ochronę przed CSRF w ustawieniach aplikacji, aby zapobiec atakom CSRF.
- HTTPS: Zawsze używaj HTTPS do szyfrowania komunikacji między serwerem a klientami.
- Walidacja danych wejściowych: Waliduj wszystkie dane wejściowe od użytkownika, aby zapobiec atakom typu "injection" i innym podatnościom.
- Regularne audyty bezpieczeństwa: Przeprowadzaj regularne audyty bezpieczeństwa, aby identyfikować i usuwać potencjalne podatności.
Wdrażanie (Deployment)
Wdrażanie aplikacji Tornado obejmuje kilka kroków, w tym konfigurację serwera WWW, ustawienie menedżera procesów i optymalizację wydajności.
Serwer WWW:
Możesz wdrożyć Tornado za serwerem WWW, takim jak Nginx lub Apache. Serwer WWW działa jako odwrotne proxy, przekazując przychodzące żądania do aplikacji Tornado.
Menedżer procesów:
Menedżer procesów, taki jak Supervisor lub systemd, może być używany do zarządzania procesem Tornado, zapewniając jego automatyczne ponowne uruchomienie w przypadku awarii.
Optymalizacja wydajności:
- Użyj pętli zdarzeń gotowej do produkcji: Użyj pętli zdarzeń gotowej do produkcji, takiej jak
uvloop
, aby poprawić wydajność. - Włącz kompresję gzip: Włącz kompresję gzip, aby zmniejszyć rozmiar odpowiedzi HTTP.
- Buforuj pliki statyczne: Buforuj pliki statyczne, aby zmniejszyć obciążenie serwera.
- Monitoruj wydajność: Monitoruj wydajność swojej aplikacji za pomocą narzędzi takich jak New Relic lub Prometheus.
Internacjonalizacja (i18n) i Lokalizacja (l10n)
Podczas tworzenia aplikacji dla globalnej publiczności ważne jest uwzględnienie internacjonalizacji (i18n) i lokalizacji (l10n). i18n to proces projektowania aplikacji w taki sposób, aby można ją było dostosować do różnych języków i regionów bez zmian inżynierskich. l10n to proces adaptacji zinternacjonalizowanej aplikacji do określonego języka lub regionu poprzez dodanie komponentów specyficznych dla danego locale i przetłumaczenie tekstu.
Tornado a i18n/l10n
Samo Tornado nie ma wbudowanych bibliotek i18n/l10n. Można jednak łatwo zintegrować standardowe biblioteki Pythona, takie jak `gettext`, lub bardziej zaawansowane frameworki, takie jak Babel, aby obsłużyć i18n/l10n w aplikacji Tornado.
Przykład z użyciem `gettext`:
1. **Skonfiguruj swoje locale:** Utwórz katalogi dla każdego języka, który chcesz wspierać, zawierające katalogi wiadomości (zazwyczaj pliki `.mo`).
locales/
en/LC_MESSAGES/messages.mo
fr/LC_MESSAGES/messages.mo
de/LC_MESSAGES/messages.mo
2. **Wyodrębnij ciągi znaków do tłumaczenia:** Użyj narzędzia takiego jak `xgettext`, aby wyodrębnić ciągi znaków do tłumaczenia z kodu Pythona do pliku `.po` (Portable Object). Ten plik będzie zawierał oryginalne ciągi znaków i miejsce na tłumaczenia.
xgettext -d messages -o locales/messages.po your_tornado_app.py
3. **Przetłumacz ciągi znaków:** Przetłumacz ciągi znaków w plikach `.po` dla każdego języka.
4. **Skompiluj tłumaczenia:** Skompiluj pliki `.po` do plików `.mo` (Machine Object), które są używane przez `gettext` w czasie wykonania.
msgfmt locales/fr/LC_MESSAGES/messages.po -o locales/fr/LC_MESSAGES/messages.mo
5. **Zintegruj z aplikacją 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:
# Obsłuż przypadki, gdy locale nie jest wspierane przez system
print(f"Locale {self.get_user_locale().code} nie jest wspierane")
translation = gettext.translation('messages', 'locales', languages=[self.get_user_locale().code])
translation.install()
self._ = translation.gettext
def get_current_user_locale(self):
# Logika do określenia locale użytkownika (np. z nagłówka Accept-Language, ustawień użytkownika itp.)
# To jest uproszczony przykład - będziesz potrzebować bardziej rozbudowanego rozwiązania
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. **Zmodyfikuj swoje szablony:** Użyj funkcji `_()` (powiązanej z `gettext.gettext`), aby oznaczyć ciągi znaków do tłumaczenia w szablonach.
<h1>{{ _("Witaj na naszej stronie!") }}</h1>
<p>{{ _("To jest przetłumaczony akapit.") }}</p>
Ważne kwestie dla globalnej publiczności:
- **Kodowanie znaków:** Zawsze używaj kodowania UTF-8, aby wspierać szeroki zakres znaków.
- **Formatowanie daty i czasu:** Używaj formatowania daty i czasu specyficznego dla danego locale. Funkcje `strftime` i `strptime` z Pythona mogą być używane z ustawieniami locale.
- **Formatowanie liczb:** Używaj formatowania liczb specyficznego dla danego locale (np. separatory dziesiętne, separatory tysięcy). Moduł `locale` dostarcza funkcji do tego celu.
- **Formatowanie walut:** Używaj formatowania walut specyficznego dla danego locale. Rozważ użycie biblioteki takiej jak `Babel` do bardziej zaawansowanej obsługi walut.
- **Języki od prawej do lewej (RTL):** Wspieraj języki RTL, takie jak arabski i hebrajski. Może to wymagać lustrzanego odbicia układu strony internetowej.
- **Jakość tłumaczenia:** Korzystaj z usług profesjonalnych tłumaczy, aby zapewnić dokładne i kulturowo odpowiednie tłumaczenia. Tłumaczenie maszynowe może być dobrym punktem wyjścia, ale często wymaga weryfikacji przez człowieka.
- **Wykrywanie locale użytkownika:** Zaimplementuj solidne wykrywanie locale na podstawie preferencji użytkownika, ustawień przeglądarki lub adresu IP. Zapewnij użytkownikom możliwość ręcznego wyboru preferowanego języka.
- **Testowanie:** Dokładnie przetestuj swoją aplikację z różnymi locale, aby upewnić się, że wszystko jest wyświetlane poprawnie.
Tematy zaawansowane
Niestandardowe strony błędów:
Możesz dostosować strony błędów, które Tornado wyświetla w przypadku wystąpienia błędu. Pozwala to na zapewnienie bardziej przyjaznego dla użytkownika doświadczenia i dołączenie informacji debugowania.
Ustawienia niestandardowe:
Możesz zdefiniować niestandardowe ustawienia w konfiguracji aplikacji i uzyskiwać do nich dostęp w swoich request handlerach. Jest to przydatne do przechowywania parametrów specyficznych dla aplikacji, takich jak ciągi połączeń do bazy danych lub klucze API.
Testowanie:
Dokładnie testuj swoje aplikacje Tornado, aby upewnić się, że działają poprawnie i bezpiecznie. Używaj testów jednostkowych, integracyjnych i end-to-end, aby objąć wszystkie aspekty swojej aplikacji.
Podsumowanie
Tornado to potężny i wszechstronny framework webowy, który doskonale nadaje się do tworzenia skalowalnych, wysokowydajnych aplikacji internetowych. Jego asynchroniczna architektura, wsparcie dla WebSockets i łatwe w użyciu API czynią go popularnym wyborem wśród programistów na całym świecie. Postępując zgodnie z wytycznymi i przykładami w tym kompleksowym przewodniku, możesz zacząć tworzyć własne aplikacje Tornado i korzystać z jego wielu funkcji.
Pamiętaj, aby zapoznać się z oficjalną dokumentacją Tornado w celu uzyskania najbardziej aktualnych informacji i najlepszych praktyk. Miłego kodowania!