איך משתמשים בתור המשימות של App Engine (משימות Push) באפליקציות Flask (מודול 7)

1. סקירה כללית

סדרת הServerless Migration Station של Codelabs (מדריכים מעשיים בקצב עצמי) וסרטונים קשורים נועדו לעזור להעביר מפתחים ללא שרת (serverless) של Google Cloud, באמצעות העברת אפליקציות מדור קודם באמצעות שירותי Google Cloud. כך האפליקציות שלכם יהיו יותר ניידות ויהיו לכם יותר אפשרויות וגמישות, כך שתוכלו להשתלב עם מגוון רחב יותר של מוצרי Cloud ולגשת אליהם בקלות, ולהשדרג בקלות רבה יותר לגרסאות חדשות יותר של שפות. הסדרה מתמקדת בהתחלה במשתמשי Cloud הראשונים, ובעיקר מפתחי App Engine (בסביבה סטנדרטית), אבל היא רחבה מספיק כדי לכלול פלטפורמות אחרות ללא שרת (serverless), כמו Cloud Functions ו-Cloud Run, או במקומות אחרים, אם רלוונטי.

ב-Codelab הזה מוסבר איך להשתמש במשימות דחיפה בתור המשימה של App Engine באפליקציה לדוגמה מCodelab של מודול 1. הפוסט בבלוג והסרטון של מודול 7 משלימים את המדריך הזה ומספקים סקירה כללית קצרה של התוכן במדריך.

במודול הזה נוסיף את השימוש במשימות push ולאחר מכן נעביר את השימוש הזה ל-Cloud Tasks במודול 8 ואילך ל-Python 3 ול-Cloud Datastore במודול 9. משתמשים שמשתמשים בתכונה 'תורי משימות' למשימות משיכה יועברו ל-Cloud Pub/Sub ויפנו למודולים 18-19 במקום זאת.

כאן אפשר להבין איך

  • שימוש ב-App Engine Task Queue API או בשירות בחבילה
  • הוספת השימוש במשימה בדחיפה לאפליקציית NDB בסיסית של Python 2 Flask App Engine NDB

למה תזדקק?

סקר

איך תשתמשו במדריך הזה?

לקריאה בלבד לקרוא אותו ולבצע את התרגילים

איזה דירוג מגיע לדעתך לחוויה שלך עם Python?

מתחילים בינונית בקיאים

איזה דירוג מגיע לדעתך לחוויית השימוש שלך בשירותי Google Cloud?

מתחילים בינונית בקיאים

2. רקע

תור המשימות של App Engine תומך גם במשימות דחיפה וגם במשימות משיכה. כדי לשפר את ניידות האפליקציות, צוות Google Cloud ממליץ לעבור משירותים בחבילה מדור קודם כמו 'תור המשימות', לשירותים עצמאיים אחרים ב-Cloud או לשירותים מקבילים של צד שלישי.

העברה של משימות משיכה נכללת במודולים של העברה 18-19, ואילו מודולים 7-9 מתמקדים בהעברה של משימות דחיפה. כדי לעבור ממשימות דחיפה ב-App Engine Queue המשימה, צריך להוסיף את השימוש של ה-NDB לאפליקציית Flask ו-App Engine NDB הקיימת מה-Codelab במודול 1. באפליקציה הזו, צפייה חדשה בדף רושמת ביקור חדש ומציגה את הביקורים האחרונים למשתמש. מכיוון שביקורים ישנים יותר לא מוצגים שוב ותופסים מקום ב-Datastore, ניצור משימת דחיפה כדי למחוק באופן אוטומטי את הביקורים הישנים ביותר. בהמשך יחידת הלימוד 8, נעביר את האפליקציה הזו מ'תור המשימות' אל Cloud Tasks.

המדריך הזה כולל את השלבים הבאים:

  1. הגדרה/עבודה מוקדמת
  2. עדכון ההגדרות האישיות
  3. שינוי קוד האפליקציה

3. הגדרה/עבודה מוקדמת

בקטע הזה נסביר איך:

  1. הגדרת פרויקט ב-Cloud
  2. אחזור של אפליקציה בסיסית לדוגמה
  3. (מחדש) פריסה ואימות של אפליקציה בסיסית

השלבים האלה יעזרו לכם לוודא שאתם מתחילים עם קוד תקין.

1. הגדרת הפרויקט

אם השלמתם את Codelab של Module 1, מומלץ להשתמש שוב באותו פרויקט (ובקוד). לחלופין, אפשר ליצור פרויקט חדש לגמרי או להשתמש שוב בפרויקט קיים. צריך לוודא שלפרויקט יש חשבון פעיל לחיוב וש-App Engine מופעל.

2. אחזור של אפליקציה בסיסית לדוגמה

אחת הדרישות המוקדמות ל-Codelab הזה היא להשתמש באפליקציית App Engine פעילה של 1: משלימים את Codelab של מודול 1 (מומלץ) או מעתיקים את אפליקציית מודול 1 מהמאגר. בין אם אתם משתמשים בקוד של מודול 1 שלכם או שלנו, הקוד של מודול 1 הוא המקום שבו נתחיל. ה-Codelab הזה ידריך אותך בכל שלב ויסתיים עם קוד שדומה למה שמופיע בתיקיית המאגר של מודול 7 'FINISH'.

התיקייה צריכה להיראות כמו בדוגמה הבאה, אולי עם התיקייה lib, בלי קשר לאפליקציית מודול 1 שבה משתמשים:

$ ls
README.md               main.py                 templates
app.yaml                requirements.txt

3. (Re) פריסת אפליקציות בסיסיות

מבצעים את השלבים הבאים כדי לפרוס מחדש את אפליקציית מודול 1:

  1. אם קיימת תיקייה כזו, מוחקים את התיקייה lib ומריצים אותה: pip install -t lib -r requirements.txt כדי לאכלס מחדש את lib. יכול להיות שתצטרכו להשתמש בפקודה pip2 במקום זאת, אם גרסת Python 2 וגם 3 מותקנות.
  2. חשוב לוודא שהתקנתם ואתחלתם את כלי שורת הפקודה gcloud, ושבדקתם את השימוש בו.
  3. אם אתם לא רוצים להזין את ה-PROJECT_ID בכל פעם שמופקת פקודת gcloud, צריך להגדיר את הפרויקט ב-Cloud באמצעות gcloud config set project PROJECT_ID.
  4. פריסת האפליקציה לדוגמה באמצעות gcloud app deploy
  5. מוודאים שאפליקציית מודול 1 פועלת כצפוי בלי בעיה בהצגת הביקורים האחרונים (איור בהמשך)

a7a9d2b80d706a2b.png

4. עדכון ההגדרות האישיות

אין צורך לבצע שינויים בקובצי התצורה הרגילים של App Engine (app.yaml, requirements.txt, appengine_config.py).

5. שינוי קובצי אפליקציה

קובץ האפליקציה הראשי הוא main.py, וכל העדכונים בקטע הזה קשורים לקובץ הזה. יש גם עדכון קל לתבנית האינטרנט, templates/index.html. אלה השינויים שצריך להטמיע בקטע הזה:

  1. עדכון ייבוא
  2. הוספת משימה בדחיפה
  3. הוספת handler של משימות
  4. עדכון של תבנית האינטרנט

1. עדכון ייבוא

ייבוא של google.appengine.api.taskqueue מביא לפונקציונליות של 'תור המשימות'. יש צורך גם בחלק מחבילות הספרייה הרגילות של Python:

  • אנחנו מוסיפים משימה למחיקת הביקורים הישנים ביותר, לכן האפליקציה תצטרך לטפל בחותמות זמן, כלומר שימוש ב-time וב-datetime.
  • כדי לרשום מידע שימושי לגבי ביצוע משימות, אנחנו צריכים את זה logging.

לאחר ההוספה של כל פעולות הייבוא האלה, הקוד נראה לפני ואחרי השינויים:

לפני:

from flask import Flask, render_template, request
from google.appengine.ext import ndb

אחרי:

from datetime import datetime
import logging
import time
from flask import Flask, render_template, request
from google.appengine.api import taskqueue
from google.appengine.ext import ndb

2. הוספת משימת Push (איסוף נתונים למשימה, הוספת תור למשימה חדשה)

במסמך 'תור הדחיפה' כתוב: "כדי לעבד משימה, עליך להוסיף אותה לתור הדחיפה. App Engine מספק תור ברירת מחדל לדחיפה, בשם default, שמוגדר ומוכן לשימוש עם הגדרות ברירת המחדל. אם רוצים, אפשר פשוט להוסיף את כל המשימות לתור ברירת המחדל, בלי ליצור תורים אחרים ולהגדיר אותם." ה-Codelab הזה משתמש בתור של default לצורך קיצור. למידע נוסף על הגדרת תורי דחיפה משלכם, עם מאפיינים זהים או שונים, ראו יצירת תורי דחיפה.

המטרה העיקרית של ה-Codelab הזה היא להוסיף משימה (לתור default לדחיפה) שהמטרה שלה היא למחוק ביקורים ישנים מ-Datastore שכבר לא מוצגים. אפליקציית הבסיס רושמת כל ביקור (בקשה אחת (GET) אל /) על ידי יצירת ישות Visit חדשה, ואז האפליקציה מאחזרת ומציגה את הביקורים האחרונים. הביקורים הישנים לא יוצגו יותר ולא ייעשה בהם שימוש שוב, לכן משימת הדחיפה מוחקת את כל הביקורים הישנים יותר מהישן לחדש. לשם כך, צריך לשנות מעט התנהגות של האפליקציה:

  1. כשמריצים שאילתות על הביקורים האחרונים, במקום להחזיר את הביקורים האלה מיד, משנים את האפליקציה כדי לשמור את חותמת הזמן של Visit האחרונים, הישן ביותר שמוצג — אפשר למחוק את כל הביקורים מלפני יותר מזה.
  2. אפשר ליצור משימת Push עם חותמת הזמן הזו כמטען הייעודי (Payload) ולהפנות אותה ל-handler של המשימה שאפשר לגשת אליה דרך POST HTTP אל /trim. באופן ספציפי, משתמשים בכלי עזר סטנדרטיים של Python כדי להמיר את חותמת הזמן של Datastore ולשלוח אותה (כמספר ממשי) למשימה, אבל גם לרשום אותה (כמחרוזת) ולהחזיר את המחרוזת הזו כערך סנטינל שיוצג למשתמש.

כל זה מתרחש ב-fetch_visits(), וכך נראה לפני ואחרי העדכונים:

לפני:

def fetch_visits(limit):
    return (v.to_dict() for v in Visit.query().order(
            -Visit.timestamp).fetch(limit))

אחרי:

def fetch_visits(limit):
    'get most recent visits and add task to delete older visits'
    data = Visit.query().order(-Visit.timestamp).fetch(limit)
    oldest = time.mktime(data[-1].timestamp.timetuple())
    oldest_str = time.ctime(oldest)
    logging.info('Delete entities older than %s' % oldest_str)
    taskqueue.add(url='/trim', params={'oldest': oldest})
    return (v.to_dict() for v in data), oldest_str

3. הוספת handler של משימות (הקוד שנקרא כשהמשימה רצה)

אמנם ניתן היה להשלים בקלות את המחיקה של ביקורים ישנים ב-fetch_visits(), אבל חשוב להבין שלפונקציונליות הזו אין הרבה קשר למשתמש הקצה. זוהי פונקציונליות עזר והיא מתאימה לעיבוד באופן אסינכרוני מחוץ לבקשות רגילות של האפליקציה. משתמש הקצה ייהנה מהיתרונות של שאילתות מהירות יותר כי ב-Datastore יהיה פחות מידע. יוצרים פונקציה חדשה trim(), שנקראת דרך בקשת POST בתור המשימה אל /trim, שמבצעת את הפעולות הבאות:

  1. משלים את 'הביקור הישן ביותר' מטען ייעודי (payload) של חותמת זמן
  2. מנפיקה שאילתה ב-Datastore כדי למצוא את כל הישויות הישנות יותר מחותמת הזמן הזו.
  3. הבעת הסכמה לשימוש מהיר יותר ב'מפתחות בלבד' כי לא נדרשים נתוני משתמש בפועל.
  4. מתעד את מספר הישויות למחיקה (כולל אפס).
  5. הפונקציה שולחת קריאה אל ndb.delete_multi() כדי למחוק ישויות כלשהן (המשתמש דילג אם לא).
  6. מחזירה מחרוזת ריקה (יחד עם קוד החזרה משתמע של HTTP 200).

אפשר לראות את כל התכונות האלה בקטע trim() למטה. להוסיף אותו אל main.py מיד אחרי fetch_visits():

@app.route('/trim', methods=['POST'])
def trim():
    '(push) task queue handler to delete oldest visits'
    oldest = request.form.get('oldest', type=float)
    keys = Visit.query(
            Visit.timestamp < datetime.fromtimestamp(oldest)
    ).fetch(keys_only=True)
    nkeys = len(keys)
    if nkeys:
        logging.info('Deleting %d entities: %s' % (
                nkeys, ', '.join(str(k.id()) for k in keys)))
        ndb.delete_multi(keys)
    else:
        logging.info('No entities older than: %s' % time.ctime(oldest))
    return ''   # need to return SOME string w/200

4. עדכון של תבנית האינטרנט

מעדכנים את תבנית האינטרנט, templates/index.html, עם התנאי הזה של Jinja2 כך שיוצגו חותמת הזמן הישנה ביותר אם המשתנה הזה קיים:

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}

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

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}
</body>
</html>

6. סיכום/ניקוי

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

פריסה ואימות של אפליקציה

פורסים את האפליקציה באמצעות gcloud app deploy. הפלט אמור להיות זהה לאפליקציית מודול 1, למעט שורה חדשה בתחתית המסך שמציגה אילו ביקורים יימחקו:

4aa8a2cb5f527079.png

ברכות על השלמת ה-Codelab. הקוד אמור עכשיו להתאים למה שמופיע בתיקיית המאגר של מודול 7. עכשיו הוא מוכן להעברה ל-Cloud Tasks במודול 8.

הסרת המשאבים

כללי

אם סיימתם בינתיים, מומלץ להשבית את אפליקציית App Engine כדי להימנע מצבירת חיובים. עם זאת, אם אתם רוצים להתנסות בפיצ'רים נוספים או להתנסות בהם, בפלטפורמת App Engine יש מכסה בחינם, וכל עוד אתם לא חורגים ממכסת השימוש הזו, לא נחייב אתכם. הבקשה נועדה למחשוב, אבל ייתכן שיחולו חיובים גם על שירותים רלוונטיים של App Engine. למידע נוסף, יש לעיין בדף התמחור של האפליקציה. אם ההעברה הזו כוללת שירותי ענן אחרים, הם מחויבים בנפרד. בכל מקרה, אם רלוונטי, ראו "ספציפי ל-Codelab זה" שבהמשך.

גילוי נאות מלא, פריסה בפלטפורמת מחשוב ללא שרת (serverless) של Google Cloud, כמו App Engine, כרוכה בעלויות נמוכות של build ואחסון. ל-Cloud Build יש מכסה משלה בחינם, כמו גם ל-Cloud Storage. נפח האחסון של התמונה הזו תופס חלק מהמכסה. עם זאת, ייתכן שאתם גרים באזור שאין בו תוכנית ללא תשלום כזה, לכן כדאי שתהיו מודעים לשימוש שלכם בנפח האחסון כדי למזער את העלויות הפוטנציאליות. 'תיקיות' ספציפיות של Cloud Storage כדאי לבדוק:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • הקישורים לנפח האחסון שלמעלה תלויים במאפיין PROJECT_ID ובמאפיין *LOC*שלך, לדוגמה, "us" אם האפליקציה מתארחת בארה"ב.

מצד שני, אם אתם לא מתכוונים להמשיך להשתמש באפליקציה הזו או ב-Codelabs קשורים אחרים ורוצים למחוק את הכול לחלוטין, כדאי להשבית את הפרויקט.

ספציפי ל-Codelab הזה

השירותים שמפורטים בהמשך הם ייחודיים ל-Codelab הזה. אפשר לקרוא מידע נוסף במסמכי התיעוד של כל מוצר:

השלבים הבאים

בקטע "העברה", הוספת את השימוש בתור 'דחיפת תור משימות' לאפליקציה לדוגמה של מודול 1, והוספת תמיכה במעקב אחר מבקרים, וכתוצאה מכך נוצרה האפליקציה לדוגמה של מודול 7. ההעברה הבאה תלמד אותך איך לשדרג ממשימות דחיפה ב-App Engine ל-Cloud Tasks, אם ברצונך לעשות זאת. החל מסתיו 2021, משתמשים לא צריכים יותר לעבור ל-Cloud Tasks כשהם משדרגים ל-Python 3. מידע נוסף בנושא הזה מופיע בקטע הבא.

אם אתם רוצים לעבור ל-Cloud Tasks, השלב הבא הוא Module 8 Codelab. מעבר לכך, יש גם העברות נוספות, כמו Cloud Datastore, Cloud Memorystore, Cloud Storage או Cloud Pub/Sub (תורי משיכה). יש גם העברות בין מוצרים ל-Cloud Run ול-Cloud Functions. אפשר לגשת לכל התוכן של תחנת המיגרציה ללא שרת (serverless) (codelabs, סרטונים, קוד מקור [אם הוא זמין]) במאגר בקוד פתוח.

7. העברה ל-Python 3

בסתיו 2021, צוות App Engine הרחיב את התמיכה ברבים מהשירותים בחבילה לזמני ריצה של דור שני (במקור היו זמינים רק בסביבות זמני ריצה מדור ראשון). המשמעות היא שכבר לא צריך לעבור משירותים בחבילה כמו App Engine Task Queue ל-Cloud עצמאי או ל-Cloud Tasks מקביל של צד שלישי כמו Cloud Tasks, בזמן ניוד האפליקציה ל-Python 3. כלומר, אפשר להמשיך להשתמש ב'תור המשימות' באפליקציות App Engine של Python 3 כל עוד הקוד נמצא מחדש כדי לגשת לשירותים בחבילה מהדור הבא של סביבת זמן ריצה.

תוכלו לקרוא מידע נוסף על העברת השימוש בשירותים בחבילה ל-Python 3 ב-Module 17 Codelab ובסרטון התואם שלו. הנושא הזה לא נכלל במסגרת מודול 7, אבל בהמשך מוצגות גרסאות Python 3 של מודול 1 ו-7 אפליקציות שהועברו ל-Python 3 ועדיין משתמשות ב-NDB של App Engine ובתור המשימה.

8. מקורות מידע נוספים

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

משוב או בעיות ב-Codelab

אם נתקלתם בבעיות ב-Codelab הזה, צריך קודם לחפש את הבעיה לפני השליחה. קישורים לחיפוש וליצירת בעיות חדשות:

משאבים להעברה

בטבלה למטה מופיעים הקישורים לתיקיות המאגר של מודול 2 (START) ומודול 7 (FINISH).

Codelab

ֶPython 2

ֶPython 3

יחידת לימוד 1

קוד

code (לא מוצג במדריך הזה)

יחידת לימוד 7 (Codelab זה)

קוד

code (לא מוצג במדריך הזה)

מקורות מידע אונליין

בהמשך מופיעים מקורות מידע מקוונים שעשויים להיות רלוונטיים למדריך זה:

תור המשימות של App Engine

פלטפורמת App Engine

מידע אחר בענן

סרטונים

רישיון

היצירה הזו בשימוש ברישיון Creative Commons Attribution 2.0 גנרי.