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

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

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

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

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

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

  • שימוש ב-App Engine Task Queue API/bundled service
  • הוספת שימוש במשימות Push לאפליקציית Python 2 Flask App Engine NDB בסיסית

הדרישות

סקר

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

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

איך היית מדרג את חוויית השימוש שלך ב-Python?

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

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

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

2. רקע

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

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

במדריך הזה נסביר איך:

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

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

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

  1. הגדרת פרויקט בענן
  2. קבלת אפליקציה לדוגמה של ערך בסיס
  3. (Re)Deploy and validate baseline app

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

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

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

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

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

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

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

3. (Re)Deploy baseline app

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

  1. מוחקים את התיקייה lib אם היא קיימת ומריצים את הפקודה: pip install -t lib -r requirements.txt כדי לאכלס מחדש את lib. אם מותקנות אצלכם גם Python 2 וגם Python 3, יכול להיות שתצטרכו להשתמש בפקודה pip2.
  2. חשוב לוודא שהתקנתם והפעלתם את כלי שורת הפקודה gcloud, וקראתם את הוראות השימוש בו.
  3. מגדירים את פרויקט בענן באמצעות gcloud config set project PROJECT_ID אם לא רוצים להזין את PROJECT_ID בכל פקודת gcloud שמוציאים.
  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. הוספת משימת דחיפה (איסוף נתונים למשימה, הוספת משימה חדשה לתור)

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

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

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

כל הפעולות האלה מתבצעות ב-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. ‫Calls ndb.delete_multi() to delete any entities (skipped if not).
  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 יש מכסת שימוש בחינם, ולכן כל עוד לא חורגים מרמת השימוש הזו, לא אמורים לחייב אתכם. החישוב הזה מתייחס ל-Compute, אבל יכול להיות שיהיו גם חיובים על שירותים רלוונטיים של App Engine. לכן, כדאי לעיין בדף התמחור שלו כדי לקבל מידע נוסף. אם ההעברה הזו כוללת שירותי ענן אחרים, הם יחויבו בנפרד. בכל מקרה, אם רלוונטי, כדאי לעיין בקטע 'ספציפי ל-codelab הזה' שבהמשך.

חשוב לדעת: פריסה בפלטפורמת מחשוב ללא שרת של Google Cloud, כמו App Engine, כרוכה בעלויות קלות של בנייה ואחסון. ל-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*ation, לדוגמה, us אם האפליקציה מאוחסנת בארה"ב.

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

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

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

השלבים הבאים

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

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

7. העברה ל-Python 3

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

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

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

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

בעיות או משוב לגבי Codelab

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

מקורות מידע על העברת נתונים

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

Codelab

Python 2

Python 3

מודול 1

קוד

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

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

קוד

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

משאבים באינטרנט

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

תור משימות ב-App Engine

פלטפורמת App Engine

מידע אחר על Cloud

סרטונים

רישיון

עבודה זו מורשית תחת רישיון Creative Commons שמותנה בייחוס 2.0 כללי.