עברית

חקירה מעמיקה של Tornado, פריימוורק ווב וספריית רשת אסינכרונית של פייתון. למדו כיצד לבנות יישומים סקלביליים ובעלי ביצועים גבוהים עם הסברים מפורטים, דוגמאות ושיטות עבודה מומלצות.

תיעוד Tornado: מדריך מקיף למפתחים ברחבי העולם

Tornado הוא פריימוורק ווב וספריית רשת אסינכרונית של פייתון, שפותחה במקור ב-FriendFeed. היא מתאימה במיוחד עבור long-polling, WebSockets, ויישומים אחרים הדורשים חיבור ארוך טווח לכל משתמש. קלט/פלט הרשת הלא-חוסם שלה הופך אותה לסקלבילית במיוחד ולבחירה עוצמתית לבניית יישומי ווב בעלי ביצועים גבוהים. מדריך מקיף זה ילווה אתכם דרך מושגי הליבה של Tornado ויספק דוגמאות מעשיות כדי להתחיל.

מהו Tornado?

בבסיסו, Tornado הוא פריימוורק ווב וספריית רשת אסינכרונית. בניגוד לפריימוורקים סינכרוניים מסורתיים, Tornado משתמש בארכיטקטורה מבוססת לולאת אירועים (event loop) עם תהליכון יחיד. משמעות הדבר היא שהוא יכול לטפל בחיבורים מקבילים רבים מבלי לדרוש תהליכון לכל חיבור, מה שהופך אותו ליעיל וסקלבילי יותר.

תכונות מפתח של Tornado:

הגדרת סביבת Tornado שלכם

לפני שצוללים לפיתוח עם Tornado, תצטרכו להגדיר את הסביבה שלכם. הנה מדריך שלב אחר שלב:

  1. התקינו פייתון: ודאו שמותקן אצלכם פייתון 3.6 ומעלה. ניתן להוריד אותו מאתר פייתון הרשמי (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. אתם מספקים רשימה של tuples, כאשר כל tuple מכיל תבנית 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 }} הם מצייני מיקום שיוחלפו בערכים ממשיים כאשר התבנית תעובד.

עיבוד תבניות:

כדי לעבד תבנית, השתמשו במתודת 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. הוא מנטר מתארי קבצים ושקעים (sockets) לאירועים ושולח אותם למטפלים המתאימים. בדרך כלל אין צורך ליצור אינטראקציה ישירה עם ה-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 # אפשור חיבורי 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 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 מספק מספר תכונות שיעזרו לכם לאבטח את היישומים שלכם, כולל אימות, הרשאה והגנה מפני פגיעויות ווב נפוצות.

אימות:

אימות הוא תהליך אימות זהותו של משתמש. Tornado מספק תמיכה מובנית במגוון סכימות אימות, כולל:

הרשאה:

הרשאה היא תהליך קביעה אם למשתמש יש הרשאה לגשת למשאב מסוים. ניתן לממש לוגיקת הרשאה במטפלי הבקשות שלכם כדי להגביל גישה על בסיס תפקידי משתמש או הרשאות.

שיטות עבודה מומלצות לאבטחה:

פריסה (Deployment)

פריסת יישום Tornado כוללת מספר שלבים, כולל הגדרת שרת ווב, הגדרת מנהל תהליכים ואופטימיזציה של ביצועים.

שרת ווב:

ניתן לפרוס את Tornado מאחורי שרת ווב כמו Nginx או Apache. שרת הווב פועל כפרוקסי הפוך (reverse proxy), ומעביר בקשות נכנסות ליישום ה-Tornado.

מנהל תהליכים:

ניתן להשתמש במנהל תהליכים כמו Supervisor או systemd כדי לנהל את תהליך ה-Tornado, ולהבטיח שהוא יופעל מחדש באופן אוטומטי אם הוא קורס.

אופטימיזציית ביצועים:

בינאום (i18n) ולוקליזציה (l10n)

כאשר בונים יישומים לקהל גלובלי, חשוב לקחת בחשבון בינאום (i18n) ולוקליזציה (l10n). i18n הוא תהליך תכנון יישום כך שניתן להתאימו לשפות ואזורים שונים ללא שינויים הנדסיים. l10n הוא תהליך התאמת יישום שעבר בינאום לשפה או אזור ספציפיים על ידי הוספת רכיבים ספציפיים ללוקאל ותרגום טקסט.

Tornado ו-i18n/l10n

ל-Tornado עצמה אין ספריות i18n/l10n מובנות. עם זאת, ניתן לשלב בקלות ספריות פייתון סטנדרטיות כמו `gettext` או פריימוורקים מתוחכמים יותר כמו Babel כדי לטפל ב-i18n/l10n בתוך יישום ה-Tornado שלכם.

דוגמה באמצעות `gettext`:

1. **הגדירו את הלוקאלים שלכם:** צרו ספריות עבור כל שפה שברצונכם לתמוך בה, המכילות קטלוגי הודעות (בדרך כלל קבצי `.mo`).

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

2. **חלצו מחרוזות לתרגום:** השתמשו בכלי כמו `xgettext` כדי לחלץ מחרוזות הניתנות לתרגום מקוד הפייתון שלכם לקובץ `.po` (Portable Object). קובץ זה יכיל את המחרוזות המקוריות ומצייני מיקום לתרגומים.

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

3. **תרגמו את המחרוזות:** תרגמו את המחרוזות בקבצי ה-`.po` עבור כל שפה.

4. **הידור התרגומים:** הדרו (compile) את קבצי ה-`.po` לקבצי `.mo` (Machine Object) המשמשים את `gettext` בזמן ריצה.

msgfmt locales/he/LC_MESSAGES/messages.po -o locales/he/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"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):
  # לוגיקה לקביעת הלוקאל של המשתמש (למשל, מכותרת 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>{{ _("Welcome to our website!") }}</h1>
<p>{{ _("This is a translated paragraph.") }}</p>

שיקולים חשובים לקהלים גלובליים:

נושאים מתקדמים

דפי שגיאה מותאמים אישית:

ניתן להתאים אישית את דפי השגיאה ש-Tornado מציג כאשר מתרחשת שגיאה. זה מאפשר לכם לספק חווית משתמש ידידותית יותר ולכלול מידע לניפוי באגים.

הגדרות מותאמות אישית:

ניתן להגדיר הגדרות מותאמות אישית בתצורת היישום שלכם ולגשת אליהן במטפלי הבקשות. זה שימושי לאחסון פרמטרים ספציפיים ליישום, כגון מחרוזות חיבור למסד נתונים או מפתחות API.

בדיקות:

בדקו היטב את יישומי ה-Tornado שלכם כדי להבטיח שהם פועלים כראוי ובאופן מאובטח. השתמשו בבדיקות יחידה, בדיקות אינטגרציה ובדיקות קצה-לקצה כדי לכסות את כל ההיבטים של היישום שלכם.

סיכום

Tornado הוא פריימוורק ווב עוצמתי ורב-תכליתי המתאים היטב לבניית יישומי ווב סקלביליים ובעלי ביצועים גבוהים. הארכיטקטורה האסינכרונית שלו, התמיכה ב-WebSocket וה-API הקל לשימוש הופכים אותו לבחירה פופולרית בקרב מפתחים ברחבי העולם. על ידי ביצוע ההנחיות והדוגמאות במדריך מקיף זה, תוכלו להתחיל לבנות יישומי Tornado משלכם ולנצל את תכונותיו הרבות.

זכרו לעיין בתיעוד הרשמי של Tornado לקבלת המידע המעודכן ביותר ושיטות העבודה המומלצות. קידוד נעים!