מעבר ממשימות המשיכה לתור המשימות של App Engine אל Cloud Pub/Sub (מודול 19)

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

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

מטרת ה-Codelab הזה היא להראות למפתחי Python 2 App Engine איך להעביר משימות משיכה (pull) מ-App Engine Task Queue אל Cloud Pub/Sub. יש גם העברה מרומזת מ-App Engine NDB אל Cloud NDB לגישה ל-Datastore (הנושא הזה מכוסה בעיקר במודול 2), וגם שדרוג ל-Python 3.

במודול 18 למדתם איך להוסיף לאפליקציה שימוש במשימות pull. במודול הזה תעבירו את השימוש הזה ל-Cloud Pub/Sub באפליקציה המוגמרת של מודול 18. משתמשים בתורי משימות למשימות push יבצעו מיגרציה ל-Cloud Tasks ויעיינו במודולים 7-9 במקום זאת.

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

הדרישות

סקר

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

רק לקרוא לקרוא ולהשלים את התרגילים

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

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

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

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

2. רקע

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

מודולים 7-9 עוסקים בהעברה של משימות מסוג push, ומודולים 18-19 מתמקדים בהעברה של משימות מסוג pull. ‫Cloud Tasks מתאים יותר למשימות push ב-Task Queue, אבל Pub/Sub לא מתאים למשימות pull ב-Task Queue.

ל-Pub/Sub יש יותר תכונות מאשר לפונקציונליות של משיכת נתונים שזמינה ב-Task Queue. לדוגמה, ל-Pub/Sub יש גם פונקציונליות של push, אבל Cloud Tasks דומה יותר למשימות push של Task Queue, ולכן push של Pub/Sub לא נכלל באף אחד ממודולי ההעברה. ב-Codelab הזה (מודול 19) נדגים איך להחליף את מנגנון התורים מתורי משיכה (pull queue) של Task Queue ל-Pub/Sub, וגם איך לבצע מיגרציה מ-App Engine NDB ל-Cloud NDB כדי לגשת ל-Datastore, תוך חזרה על המיגרציה ממודול 2.

הקוד של מודול 18 מוצג כדוגמה לאפליקציית Python 2, אבל המקור עצמו תואם ל-Python 2 ול-Python 3, והוא נשאר כזה גם אחרי המעבר ל-Cloud Pub/Sub (ול-Cloud NDB) כאן במודול 19.

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

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

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

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

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

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

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

אם השלמתם את ה-Codelab של מודול 18, תוכלו להשתמש שוב באותו פרויקט (ובאותו קוד). אפשר גם ליצור פרויקט חדש לגמרי או להשתמש מחדש בפרויקט קיים אחר. צריך לוודא שלפרויקט יש חשבון לחיוב פעיל ואפליקציית App Engine מופעלת. כדאי למצוא את מזהה הפרויקט כי תצטרכו אותו במהלך ה-codelab הזה, ותצטרכו להשתמש בו בכל פעם שתיתקלו במשתנה PROJECT_ID.

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

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

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

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

3. (Re)Deploy and validate baseline app

כדי לפרוס את האפליקציה Module 18:

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

b667551dcbab1a09.png

לפני שמבצעים העברה של אפליקציית הדוגמה Module 18, צריך קודם להפעיל את שירותי הענן שבהם האפליקציה ששונתה תשתמש.

4. הפעלת שירותים או ממשקי API חדשים של Google Cloud

האפליקציה הישנה השתמשה בשירותים בחבילה של App Engine שלא דורשים הגדרה נוספת, אבל שירותי Cloud עצמאיים כן דורשים הגדרה, והאפליקציה המעודכנת תשתמש גם ב-Cloud Pub/Sub וגם ב-Cloud Datastore (באמצעות ספריית הלקוח Cloud NDB). ל-App Engine ולשני ממשקי ה-API של Cloud יש מכסות של מסלול 'תמיד בחינם', ולכן אם לא תחרגו מהמגבלות האלה, לא תחויבו על השלמת המדריך הזה. אפשר להפעיל את Cloud APIs דרך מסוף Cloud או דרך שורת הפקודה, בהתאם להעדפה שלכם.

מתוך Cloud Console

נכנסים אל דף הספרייה של API Manager (בפרויקט הנכון) ב-Cloud Console, ומחפשים את Cloud Datastore ואת Cloud Pub/Sub APIs באמצעות סרגל החיפוש באמצע הדף:

c7a740304e9d35b.png

לוחצים על הלחצן הפעלה לכל API בנפרד. יכול להיות שתתבקשו להזין פרטי חיוב. לדוגמה, זהו הדף Cloud Pub/Sub API Library:

1b6c0a2a73124f6b.jpeg

משורת הפקודה

הפעלת ממשקי API דרך המסוף מספקת מידע חזותי, אבל יש אנשים שמעדיפים את שורת הפקודה. מריצים את הפקודה gcloud services enable pubsub.googleapis.com datastore.googleapis.com כדי להפעיל את שני ממשקי ה-API בו-זמנית:

$ gcloud services enable pubsub.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

יכול להיות שתתבקשו להזין פרטי חיוב. אם רוצים להפעיל ממשקי Cloud API אחרים ולדעת מהם מזהי ה-URI שלהם, אפשר למצוא אותם בחלק התחתון של כל דף בספריית ה-API. לדוגמה, אפשר לראות את pubsub.googleapis.com כ'שם השירות' בחלק התחתון של הדף Pub/Sub שמופיע למעלה.

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

4. יצירת משאבי Pub/Sub

סיכום של סדר הפעולות בתהליך העבודה של תור המשימות מתוך מודול 18:

  1. במודול 18 נעשה שימוש בקובץ queue.yaml כדי ליצור תור משיכה בשם pullq.
  2. האפליקציה מוסיפה משימות לתור המשימות כדי לעקוב אחרי המבקרים.
  3. העובד מעבד את המשימות בסופו של דבר, והוא מושכר לפרק זמן מוגבל (שעה).
  4. המשימות מבוצעות כדי לסכם את מספר המבקרים האחרון.
  5. המשימות נמחקות מהתור אחרי שהן מסתיימות.

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

המינוח של App Engine Task Queue (שליפה) לעומת Cloud Pub/Sub

כדי לעבור ל-Pub/Sub, צריך לבצע שינוי קל במינוח. בהמשך מפורטות הקטגוריות העיקריות יחד עם מונחים רלוונטיים משני המוצרים. מומלץ לעיין גם במדריך להעברת נתונים (מיגרציה), שכולל השוואות דומות.

  • מבנה נתונים של תור: ב-Task Queue, הנתונים נכנסים לתורי משימות מסוג pull. ב-Pub/Sub, הנתונים נכנסים לנושאים.
  • יחידות של נתונים בתור: משימות משיכה עם Task Queue נקראות הודעות עם Pub/Sub.
  • מעבדי נתונים: ב-Task Queue, עובדים מקבלים גישה למשימות מסוג pull. ב-Pub/Sub, צריך מינויים/מנויים כדי לקבל הודעות.
  • חילוץ נתונים: שליפת משימת שליפה זהה לשליפת הודעה מנושא (באמצעות מינוי).
  • ניקוי או השלמה: מחיקה של משימה בתור משימות בתור משימות מסוג pull אחרי שמסיימים אותה דומה לאישור של הודעת Pub/Sub

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

  1. במקום תור משיכה (pull queue), האפליקציה משתמשת בנושא בשם pullq.
  2. במקום להוסיף משימות לתור משיכות, האפליקציה שולחת הודעות לנושא (pullq).
  3. במקום שעובד ישכור משימות מתור משיכה (pull queue), מנוי בשם worker שולף הודעות מהנושא pullq.
  4. האפליקציה מעבדת את נתוני ההודעות ומגדילה את מספר המבקרים ב-Datastore.
  5. במקום למחוק משימות מתור משיכה (pull queue), האפליקציה מאשרת את ההודעות שעברו עיבוד.

ב-Task Queue, ההגדרה כוללת יצירה של תור משימות מסוג pull. ב-Pub/Sub, ההגדרה מחייבת יצירה של נושא ושל מינוי. במודול 18, עיבדנו את queue.yaml מחוץ להרצת האפליקציה. עכשיו צריך לעשות את אותו הדבר עם Pub/Sub.

יש שלוש אפשרויות ליצירת נושאים ומינויים:

  1. ממסוף Cloud
  2. משורת הפקודה, או
  3. מקוד (סקריפט קצר של Python)

בוחרים באחת מהאפשרויות שלמטה ופועלים לפי ההוראות המתאימות כדי ליצור את משאבי ה-Pub/Sub.

ממסוף Cloud

כדי ליצור נושא מ-Cloud Console:

  1. נכנסים לדף Pub/Sub Topics במסוף Cloud.
  2. לוחצים על יצירת נושא בחלק העליון. נפתח חלון דו-שיח חדש (ראו תמונה למטה).
  3. בשדה Topic ID, מזינים pullq.
  4. מבטלים את הסימון של כל האפשרויות המסומנות ובוחרים באפשרות מפתח הצפנה בניהול Google.
  5. לוחצים על הלחצן ליצירת נושא.

כך נראית תיבת הדו-שיח ליצירת נושא:

a05cfdbf64571ceb.png

אחרי שיצרתם נושא, צריך ליצור מינוי לנושא הזה:

  1. נכנסים לדף המינויים של Pub/Sub במסוף Cloud.
  2. לוחצים על יצירת מינוי בחלק העליון (ראו תמונה למטה).
  3. בשדה Subscription ID (מזהה המינוי), מזינים את הערך worker.
  4. בתפריט הנפתח Select a Cloud Pub/Sub topic, בוחרים באפשרות pullq ורושמים את 'שם הנתיב המלא', לדוגמה, projects/PROJECT_ID/topics/pullq
  5. בסוג המסירה, בוחרים באפשרות משיכה.
  6. משאירים את כל שאר האפשרויות כמו שהן ולוחצים על הלחצן יצירה.

כך נראה מסך יצירת המינוי:

c5444375c20b0618.jpeg

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

משורת הפקודה

משתמשי Pub/Sub יכולים ליצור נושאים ומינויים באמצעות הפקודות gcloud pubsub topics create TOPIC_ID ו-gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID, בהתאמה. הפעלת הפקודות האלה עם TOPIC_ID של pullq ו-SUBSCRIPTION_ID של worker מניבה את הפלט הבא עבור הפרויקט PROJECT_ID:

$ gcloud pubsub topics create pullq
Created topic [projects/PROJECT_ID/topics/pullq].

$ gcloud pubsub subscriptions create worker --topic=pullq
Created subscription [projects/PROJECT_ID/subscriptions/worker].

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

מקוד (סקריפט קצר של Python)

דרך נוספת לאוטומציה של יצירת נושאים ומינויים היא באמצעות Pub/Sub API בקוד המקור. בהמשך מופיע הקוד של maker.py script בתיקיית המאגר Module 19.

from __future__ import print_function
import google.auth
from google.api_core import exceptions
from google.cloud import pubsub

_, PROJECT_ID = google.auth.default()
TOPIC = 'pullq'
SBSCR = 'worker'
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

def make_top():
    try:
        top = ppc_client.create_topic(name=TOP_PATH)
        print('Created topic %r (%s)' % (TOPIC, top.name))
    except exceptions.AlreadyExists:
        print('Topic %r already exists at %r' % (TOPIC, TOP_PATH))

def make_sub():
    try:
        sub = psc_client.create_subscription(name=SUB_PATH, topic=TOP_PATH)
        print('Subscription created %r (%s)' % (SBSCR, sub.name))
    except exceptions.AlreadyExists:
        print('Subscription %r already exists at %r' % (SBSCR, SUB_PATH))
    try:
        psc_client.close()
    except AttributeError:  # special Py2 handler for grpcio<1.12.0
        pass

make_top()
make_sub()

הפעלת הסקריפט הזה מניבה את הפלט הצפוי (בתנאי שאין שגיאות):

$ python3 maker.py
Created topic 'pullq' (projects/PROJECT_ID/topics/pullq)
Subscription created 'worker' (projects/PROJECT_ID/subscriptions/worker)

קריאה ל-API כדי ליצור משאבים שכבר קיימים גורמת לחריגה google.api_core.exceptions.AlreadyExists שמוחזרת על ידי ספריית הלקוח, ומטופלת בצורה חלקה על ידי הסקריפט:

$ python3 maker.py
Topic 'pullq' already exists at 'projects/PROJECT_ID/topics/pullq'
Subscription 'worker' already exists at 'projects/PROJECT_ID/subscriptions/worker'

אם אתם חדשים ב-Pub/Sub, כדאי לקרוא את מאמר המידע על הארכיטקטורה של Pub/Sub.

5. עדכון ההגדרות

העדכונים בהגדרות כוללים שינוי של קובצי הגדרות שונים וגם יצירה של תורים מקבילים של App Engine, אבל בסביבה העסקית של Cloud Pub/Sub.

מחיקת queue.yaml

אנחנו מפסיקים את השימוש ב-Task Queue לחלוטין, לכן צריך למחוק את queue.yaml כי Pub/Sub לא משתמש בקובץ הזה. במקום ליצור תור משיכה, תיצרו נושאמינוי) ב-Pub/Sub.

requirements.txt

מוסיפים את google-cloud-ndb ואת google-cloud-pubsub ל-requirements.txt כדי להצטרף ל-flask ממודול 18. יחידת לימוד 19 המעודכנת requirements.txt אמורה להיראות כך:

flask
google-cloud-ndb
google-cloud-pubsub

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

app.yaml

השינויים ב-app.yaml שונים בהתאם להחלטה שלכם אם להישאר עם Python 2 או לשדרג ל-Python 3.

Python 2

העדכון של requirements.txt שמופיע למעלה מוסיף שימוש בספריות הלקוח של Google Cloud. הם דורשים תמיכה נוספת מ-App Engine, כלומר כמה ספריות מובנות, setuptools ו-grpcio. כדי להשתמש בספריות מובנות, צריך להוסיף קטע libraries בקובץ app.yaml ולציין את מספרי הגרסאות של הספריות, או את הערך latest כדי להשתמש בגרסה העדכנית ביותר שזמינה בשרתי App Engine. מודול 18 app.yaml עדיין לא כולל אחד מהקטעים האלה:

לפני:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

מוסיפים libraries section ל-app.yaml יחד עם רשומות ל-setuptools ול-grpcio, ובוחרים את הגרסאות העדכניות שלהם. כדאי גם להוסיף placeholder runtime ל-Python 3, עם הערה שמוציאה אותו משימוש, לצד גרסה עדכנית של 3.x, למשל 3.10, בזמן הכתיבה. בעקבות השינויים האלה, app.yaml נראה עכשיו כך:

אחרי:

#runtime: python310
runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: setuptools
  version: latest
- name: grpcio
  version: latest

Python 3

למשתמשי Python 3 ול-app.yaml, מדובר בהסרת פריטים. בקטע הזה נמחק את הקטע handlers, את ההנחיות threadsafe ו-api_version, ולא ניצור קטע libraries.

סביבות זמן ריצה מהדור השני לא מספקות ספריות מובנות של צד שלישי, ולכן לא צריך להוסיף קטע libraries ב-app.yaml. בנוסף, כבר לא נדרשת העתקה (שנקראת לפעמים vendoring או self-bundling) של חבילות צד שלישי שאינן מוכללות. צריך לציין בסעיף requirements.txt רק ספריות של צד שלישי שהאפליקציה משתמשת בהן.

הקטע handlers בקובץ app.yaml מיועד לציון רכיבי handler של אפליקציות (סקריפטים) וקבצים סטטיים. מכיוון שסביבת זמן הריצה של Python 3 מחייבת מסגרות אינטרנט לבצע ניתוב משלהן, צריך לשנות את כל המטפלים בסקריפטים ל-auto. אם האפליקציה שלכם (כמו באפליקציה של מודול 18) לא מציגה קבצים סטטיים, כל המסלולים יהיו auto, ולכן הם לא יהיו רלוונטיים. לכן גם אין צורך בקטע handlers, אז מוחקים אותו.

בנוסף, ההנחיות threadsafe ו-api_version לא נמצאות בשימוש ב-Python 3, לכן צריך למחוק גם אותן. השורה התחתונה היא שצריך למחוק את כל החלקים של app.yaml כך שתישאר רק ההוראה runtime, שמציינת גרסה עדכנית של Python 3, למשל 3.10. כך נראה app.yaml לפני ואחרי העדכונים האלה:

לפני:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

אחרי:

runtime: python310

למי שלא מוכן למחוק הכול מ-app.yaml for Python 3, סיפקנו קובץ חלופי בתיקיית המאגר של מודול 19.app3.yaml אם אתם רוצים להשתמש בו במקום זאת לפריסות, הקפידו לצרף את שם הקובץ הזה לסוף הפקודה: gcloud app deploy app3.yaml (אחרת, ברירת המחדל תהיה פריסה של האפליקציה עם קובץ Python 2 app.yaml שלא שיניתם).

appengine_config.py

אם משדרגים ל-Python 3, אין צורך ב-appengine_config.py, ולכן אפשר למחוק אותו. הסיבה לכך היא שבתמיכה בספריות של צד שלישי נדרש רק לציין אותן ב-requirements.txt. משתמשי Python 2, כדאי להמשיך לקרוא.

במודול 18 appengine_config.py יש קוד מתאים לתמיכה בספריות של צד שלישי, לדוגמה, Flask וספריות הלקוח של Cloud שנוספו ל-requirements.txt:

לפני:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

עם זאת, הקוד הזה לבדו לא מספיק כדי לתמוך בספריות המובנות שנוספו (setuptools, grpcio). צריך להוסיף עוד כמה שורות, ולכן צריך לעדכן את appengine_config.py כך שייראה כך:

אחרי:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

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

עדכוני הגדרות אחרים

אם יש לכם תיקייה בשם lib, מחקו אותה. אם אתם משתמשים ב-Python 2, כדי למלא מחדש את התיקייה lib, מריצים את הפקודה הבאה:

pip install -t lib -r requirements.txt  # or pip2

אם גם Python 2 וגם Python 3 מותקנים במערכת הפיתוח, יכול להיות שתצטרכו להשתמש בפקודה pip2 במקום בפקודה pip.

6. שינוי קוד האפליקציה

בקטע הזה מוצגים עדכונים לקובץ האפליקציה הראשי, main.py, שבהם השימוש בתורי משימות של App Engine Task Queue מוחלף ב-Cloud Pub/Sub. לא בוצעו שינויים בתבנית האינטרנט, templates/index.html. שתי האפליקציות צריכות לפעול בצורה זהה ולהציג את אותם נתונים.

עדכון ייבוא והפעלה

יש כמה עדכונים לגבי ייבוא והפעלה:

  1. בייבוא, מחליפים את App Engine NDB ואת Task Queue ב-Cloud NDB וב-Pub/Sub.
  2. שינוי השם של pullq משם QUEUE לשם TOPIC.
  3. במשימות מסוג pull, העובד שכר אותן לשעה, אבל ב-Pub/Sub, פסק הזמן נמדד לפי כל הודעה, ולכן צריך למחוק את הקבוע HOUR.
  4. כדי להשתמש ב-Cloud APIs, צריך להפעיל לקוח API. לכן, צריך להפעיל לקוחות ל-Cloud NDB ול-Cloud Pub/Sub. האחרון מספק לקוחות גם לנושאים וגם למינויים.
  5. כדי להשתמש ב-Pub/Sub, צריך את מזהה הפרויקט בענן, לכן צריך לייבא אותו מ-google.auth.default().
  6. ב-Pub/Sub נדרשים 'שמות נתיבים מלאים' לנושאים ולמינויים, ולכן צריך ליצור אותם באמצעות *_path() פונקציות הנוחות.

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

לפני:

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

HOUR = 3600
LIMIT = 10
TASKS = 1000
QNAME = 'pullq'
QUEUE = taskqueue.Queue(QNAME)
app = Flask(__name__)

אחרי:

from flask import Flask, render_template, request
import google.auth
from google.cloud import ndb, pubsub

LIMIT = 10
TASKS = 1000
TOPIC = 'pullq'
SBSCR = 'worker'

app = Flask(__name__)
ds_client  = ndb.Client()
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
_, PROJECT_ID = google.auth.default()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

עדכונים במודל הנתונים

מודל הנתונים של Visit לא משתנה. כדי לגשת ל-Datastore, צריך להשתמש במנהל ההקשר של לקוח Cloud NDB API,‏ ds_client.context(). בקוד, המשמעות היא שצריך לעטוף את הקריאות ל-Datastore גם ב-store_visit() וגם ב-fetch_visits() בתוך בלוקים של Python with. העדכון הזה זהה למה שמוסבר במודול 2.

השינוי הכי רלוונטי ל-Pub/Sub הוא החלפת הוספה לתור של משימת שליפה של Task Queue בפרסום של הודעת Pub/Sub בנושא pullq. בהמשך מופיע הקוד לפני ואחרי ביצוע העדכונים האלה:

לפני:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit in Datastore and queue request to bump visitor count'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    QUEUE.add(taskqueue.Task(payload=remote_addr, method='PULL'))

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

אחרי:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit in Datastore and queue request to bump visitor count'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    ppc_client.publish(TOP_PATH, remote_addr.encode('utf-8'))

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

עדכונים במודל הנתונים VisitorCount

מודל הנתונים של VisitorCount לא משתנה וfetch_counts(), למעט עטיפת השאילתה שלו ב-Datastore בתוך בלוק with, כפי שמוצג בהמשך:

לפני:

class VisitorCount(ndb.Model):
    visitor = ndb.StringProperty(repeated=False, required=True)
    counter = ndb.IntegerProperty()

def fetch_counts(limit):
    'get top visitors'
    return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)

אחרי:

class VisitorCount(ndb.Model):
    visitor = ndb.StringProperty(repeated=False, required=True)
    counter = ndb.IntegerProperty()

def fetch_counts(limit):
    'get top visitors'
    with ds_client.context():
        return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)

עדכון קוד העובד

הקוד של העובד מתעדכן כך ש-NDB מוחלף ב-Cloud NDB ו-Task Queue מוחלף ב-Pub/Sub, אבל תהליך העבודה נשאר זהה.

  1. עוטפים את הקריאות ל-Datastore בבלוק with של מנהל ההקשר Cloud NDB.
  2. ניקוי תור המשימות כולל מחיקה של כל המשימות מתור המשימות מסוג pull. ב-Pub/Sub, מזהי אישור נאספים ב-acks ואז נמחקים או מאושרים בסוף.
  3. משימות מסוג pull בתור למשימות מושכרות באופן דומה למשימות מסוג pull ב-Pub/Sub. מחיקה של משימות מסוג pull מתבצעת באמצעות אובייקטים של משימות, אבל מחיקה של הודעות Pub/Sub מתבצעת באמצעות מזהי האישור שלהן.
  4. מטענים ייעודיים (payloads) של הודעות Pub/Sub דורשים בייטים (לא מחרוזות Python), ולכן יש קידוד ופענוח של UTF-8 כשמפרסמים הודעות בנושא מסוים ומושכים הודעות ממנו, בהתאמה.

מחליפים את log_visitors() בקוד המעודכן שמופיע בהמשך, שמיישם את השינויים שמתוארים למעלה:

לפני:

@app.route('/log')
def log_visitors():
    'worker processes recent visitor counts and updates them in Datastore'
    # tally recent visitor counts from queue then delete those tasks
    tallies = {}
    tasks = QUEUE.lease_tasks(HOUR, TASKS)
    for task in tasks:
        visitor = task.payload
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if tasks:
        QUEUE.delete_tasks(tasks)

    # increment those counts in Datastore and return
    for visitor in tallies:
        counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
        if not counter:
            counter = VisitorCount(visitor=visitor, counter=0)
            counter.put()
        counter.counter += tallies[visitor]
        counter.put()
    return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
            len(tasks), len(tallies))

אחרי:

@app.route('/log')
def log_visitors():
    'worker processes recent visitor counts and updates them in Datastore'
    # tally recent visitor counts from queue then delete those tasks
    tallies = {}
    acks = set()
    rsp = psc_client.pull(subscription=SUB_PATH, max_messages=TASKS)
    msgs = rsp.received_messages
    for rcvd_msg in msgs:
        acks.add(rcvd_msg.ack_id)
        visitor = rcvd_msg.message.data.decode('utf-8')
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if acks:
        psc_client.acknowledge(subscription=SUB_PATH, ack_ids=acks)
    try:
        psc_client.close()
    except AttributeError:  # special handler for grpcio<1.12.0
        pass

    # increment those counts in Datastore and return
    if tallies:
        with ds_client.context():
            for visitor in tallies:
                counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
                if not counter:
                    counter = VisitorCount(visitor=visitor, counter=0)
                    counter.put()
                counter.counter += tallies[visitor]
                counter.put()
    return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
            len(msgs), len(tallies))

לא חלים שינויים ב-handler הראשי של האפליקציה root(). גם בקובץ תבנית ה-HTML, ‏ templates/index.html, לא נדרשים שינויים, כך שכל העדכונים הדרושים כלולים כאן. הגעתם אל אפליקציית מודול 19 החדשה באמצעות Cloud Pub/Sub.

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

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

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

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

b667551dcbab1a09.png

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

אפשר להריץ את הפקודה הזו באמצעות שירות לקצה העורפי של App Engine, עבודת cron, גלישה אל /log או שליחת בקשת HTTP משורת הפקודה. הנה דוגמה להפעלה ופלט של קריאה לקוד של ה-worker עם curl (צריך להחליף את PROJECT_ID):

$ curl https://PROJECT_ID.appspot.com/log
DONE (with 1 task[s] logging 1 visitor[s])

המספר המעודכן ישתקף בביקור הבא באתר. זהו!

הסרת המשאבים

כללי

אם סיימתם לעכשיו, מומלץ להשבית את האפליקציה שלכם ב-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 הזה. מידע נוסף זמין במסמכי התיעוד של כל מוצר:

  • לרכיבים שונים של Cloud Pub/Sub יש תוכנית בחינם. כדי לקבל מושג טוב יותר לגבי ההשלכות על העלויות, כדאי לבדוק את דף התמחור.
  • שירות App Engine Datastore מסופק על ידי Cloud Datastore (Cloud Firestore במצב Datastore), שגם לו יש רמת שירות בחינם. מידע נוסף זמין במחירון שלו.

השלבים הבאים

בנוסף למדריך הזה, יש מודולים אחרים להעברה שמתמקדים במעבר משירותים קודמים בחבילה, שכדאי לעיין בהם:

‫App Engine כבר לא הפלטפורמה היחידה ללא שרת ב-Google Cloud. אם יש לכם אפליקציית App Engine קטנה או אפליקציה עם פונקציונליות מוגבלת ואתם רוצים להפוך אותה למיקרו-שירות עצמאי, או אם אתם רוצים לפצל אפליקציה מונוליטית לכמה רכיבים שאפשר לעשות בהם שימוש חוזר, אלה סיבות טובות לשקול מעבר ל-Cloud Functions. אם יצירת קונטיינרים הפכה לחלק מתהליך העבודה של פיתוח האפליקציה, במיוחד אם היא כוללת צינור CI/CD (אינטגרציה רציפה/פריסה או מסירה רציפה), כדאי לשקול מעבר אל Cloud Run. התרחישים האלה מוסברים במודולים הבאים:

  • מעבר מ-App Engine ל-Cloud Functions: ראו מודול 11
  • מעבר מ-App Engine ל-Cloud Run: אפשר לעיין במודול 4 כדי להעביר את האפליקציה לקונטיינר באמצעות Docker, או במודול 5 כדי לעשות זאת בלי קונטיינרים, בלי ידע ב-Docker או בלי Dockerfiles

המעבר לפלטפורמה אחרת בלי שרת (serverless) הוא אופציונלי, ומומלץ לבדוק מהן האפשרויות הכי טובות לאפליקציות ולתרחישי השימוש שלכם לפני שמבצעים שינויים.

לא משנה איזה מודול העברה תבחרו, תוכלו לגשת לכל התוכן של Serverless Migration Station (סדנאות קוד, סרטונים, קוד מקור [אם זמין]) במאגר הקוד הפתוח שלו. במאגר README יש גם הנחיות לגבי ההעברות שכדאי לבצע וסדר רלוונטי של מודולי ההעברה.

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

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

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

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

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

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

Codelab

Python 2

Python 3

מודול 18

קוד

(n/a)

מודול 19 (ה-Codelab הזה)

קוד

(זהה ל-Python 2, אלא אם מעדכנים את app.yaml כמו שמתואר למעלה, ואז משתמשים ב-app3.yaml)

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

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

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

Cloud Pub/Sub

‫App Engine NDB ו-Cloud NDB‏ (Datastore)

פלטפורמת App Engine

מידע אחר על Cloud

סרטונים

רישיון

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