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

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

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

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

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

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

למה תזדקק?

סקר

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

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

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

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

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

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

2. רקע

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

במודולי העברה 7-9 מוסבר על העברת משימות דחיפה, ואילו מודולים 18-19 מתמקדים בהעברה של משימות משיכה. Cloud Tasks תואם יותר משימות לדחיפה בתור המשימה, אבל Pub/Sub דומה יותר לביצועים אנלוגיים למשימות המשיכה בתור המשימה.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3. (מחדש) פריסה ואימות של אפליקציה בסיסית

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

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

b667551dcbab1a09.png

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

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

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

ממסוף Cloud

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

c7a740304e9d35b.png

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

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 שלהם, תוכלו למצוא את מזהי ה-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' (משיכה) לעומת המונחים של Cloud Pub/Sub

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

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

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

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

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

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

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

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

ממסוף Cloud

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

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

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

a05cfdbf64571ceb.png

עכשיו, אחרי שיוצרים נושא, צריך ליצור מינוי אליו:

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

מחיקת התור.yaml

אנחנו עוברים משימוש לחלוטין את 'תור המשימות', לכן צריך למחוק את 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 ובמספרי הגרסה של הספרייה, או בקטע 'העדכנית ביותר'. את העדכונים האחרונים שזמינים בשרתי App Engine. במודול 18 app.yaml עדיין אין אחד מהקטעים האלה:

לפני:

runtime: python27
threadsafe: yes
api_version: 1

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

הוספת קטע libraries ל-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. בנוסף, אין יותר צורך להעתיק (שלפעמים נקרא ספק או קיבוץ עצמי) חבילות של צד שלישי שאינן מובנות. צריך לציין רק ספריות של צד שלישי שהאפליקציה משתמשת בהן ב-requirements.txt.

הקטע handlers ב-app.yaml מיועד לציון רכיבי handler של קבצים סטטיים ושל אפליקציות (סקריפט). סביבת זמן הריצה של Python 3 מחייבת שמסגרות אינטרנט (frameworks) יבצעו ניתוב משלהן, לכן צריך לשנות את כל הגורמים המטפלים בסקריפטים ל-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 עבור Python 3, סיפקנו קובץ חלופי app3.yaml בתיקיית המאגר של מודול 19. כדי להשתמש בו לפריסות, חשוב לצרף את שם הקובץ הזה לסוף הפקודה: 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 וגם 3, יכול להיות שאתם צריכים להשתמש ב-pip2 במקום ב-pip.

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

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

ייבוא ואתחול של עדכונים

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

  1. לצורך הייבוא, צריך להחליף את App Engine NDB ואת 'הבאים בתור' ב-Cloud NDB ו-Pub/Sub.
  2. שינוי השם של pullq מהשם של QUEUE לשם של TOPIC.
  3. במשימות משיכה, העובד השכיר אותן למשך שעה, אבל ב-Pub/Sub, הזמן הקצוב לתפוגה נמדד לפי כל הודעה, לכן מוחקים את הקבוע HOUR.
  4. ממשקי Cloud API מחייבים שימוש בלקוח API, לכן צריך ליזום אותם עבור Cloud NDB ו-Cloud Pub/Sub, וכך לספק לקוחות גם לנושאים וגם למינויים.
  5. ל-Pub/Sub נדרש מזהה הפרויקט ב-Cloud, לכן צריך לייבא ולקבל אותו מ-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 לא משתנה. כדי לגשת למאגר הנתונים, צריך להשתמש באופן מפורש במנהל ההקשר של הלקוח ב-Cloud NDB API, ds_client.context(). בקוד, המשמעות היא ששיחות Datastore ב-store_visit() וב-fetch_visits() צריכות להיות בתוך בלוקים with של Python. העדכון הזה זהה למידע במודול 2.

השינוי הרלוונטי ביותר ב-Pub/Sub הוא החלפת משימת השליפה בתור 'הבאים בתור המשימות' בפרסום של הודעת 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 ואת 'תור המשימות' ב-Pub/Sub, אבל תהליך העבודה שלו לא משתנה.

  1. העברת קריאות ל-Datastore בבלוק with של המרכז לניהול ההקשר של Cloud NDB.
  2. ניקוי תור המשימות כולל מחיקת כל המשימות מתור המשיכה. עם Pub/Sub, 'מזהי אישור' נאספים ב-acks ואז נמחקים או מקבלים אישור בסוף.
  3. משימות משיכה דרך 'הבאים בתור' חתוכות באותו אופן שבו נשלפות הודעות Pub/Sub. בזמן המחיקה של משימות המשיכה עם האובייקטים של המשימה עצמם, הודעות 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, אין צורך לבצע שינויים, לכן התהליך הזה כולל את כל העדכונים הנחוצים. מזל טוב, הגעת לאפליקציית Module 19 החדשה באמצעות Cloud Pub/Sub.

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

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

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

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

b667551dcbab1a09.png

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

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

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

לאחר מכן, הספירה המעודכנת תופיע בביקור הבא באתר. זהו!

הסרת המשאבים

כללי

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

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

השלבים הבאים

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

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

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

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

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

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

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

בעיות/משוב על Codelabs

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

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

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

Codelab

ֶPython 2

ֶPython 3

יחידת לימוד 18

קוד

(לא רלוונטי)

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

קוד

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

הפניות אונליין

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

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

Cloud Pub/Sub

App Engine NDB ו-Cloud NDB (Datastore)

פלטפורמת App Engine

מידע אחר בענן

סרטונים

רישיון

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