תחילת העבודה עם gRPC-Python

1. מבוא

ב-codelab הזה תשתמשו ב-gRPC-Python כדי ליצור לקוח ושרת שיהוו את הבסיס לאפליקציה למיפוי מסלולים שנכתבה ב-Python.

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

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

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

מה תלמדו

  • איך משתמשים ב-Protocol Buffers כדי להגדיר API של שירות.
  • איך ליצור לקוח ושרת מבוססי gRPC מהגדרה של Protocol Buffers באמצעות יצירת קוד אוטומטית.
  • הבנה של תקשורת בין שרתים ללקוחות באמצעות gRPC.

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

‫2. לפני שמתחילים

מה נדרש

  • Python בגרסה 3.9 ואילך. מומלץ להשתמש ב-Python 3.13. הוראות התקנה ספציפיות לפלטפורמה מופיעות במאמר Python Setup and Usage (הגדרה ושימוש ב-Python). אפשר גם להתקין Python שאינו מערכת באמצעות כלים כמו uv או pyenv.
  • pip להתקנת חבילות Python.
  • venv כדי ליצור סביבות וירטואליות של Python.

החבילות ensurepip ו-venv הן חלק מהספרייה הרגילה של Python, ובדרך כלל הן זמינות כברירת מחדל.

עם זאת, חלק מההפצות שמבוססות על Debian (כולל Ubuntu) בוחרות להחריג אותן כשמפיצים מחדש את Python. כדי להתקין את החבילות, מריצים את הפקודה:

sudo apt install python3-pip python3-venv

קבל את הקוד

כדי לייעל את הלמידה, ב-Codelab הזה מוצעת מסגרת קוד מקור מוכנה מראש שתעזור לכם להתחיל. בשלבים הבאים מוסבר איך להשלים את הבקשה, כולל יצירת קוד gRPC באמצעות התוסף grpc_tools.protoc Protocol Buffer compiler.

grpc-codelabs

קוד המקור של ה-scaffold ל-codelab הזה זמין בספרייה codelabs/grpc-python-getting-started/start_here. אם אתם מעדיפים לא להטמיע את הקוד בעצמכם, קוד המקור המלא זמין בספרייה completed.

קודם יוצרים את ספריית העבודה של ה-codelab ועוברים אליה:

mkdir grpc-python-getting-started && cd grpc-python-getting-started

מורידים ומחלצים את ה-Codelab:

curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-1/codelabs/grpc-python-getting-started/start_here

אפשרות אחרת היא להוריד את קובץ ה-‎ .zip שמכיל רק את ספריית ה-codelab ולבטל את הדחיסה שלו באופן ידני.

3. הגדרת השירות

השלב הראשון הוא להגדיר את שירות ה-gRPC של האפליקציה, את שיטת ה-RPC ואת סוגי ההודעות של הבקשה והתגובה באמצעות שפת הגדרת הממשק של Protocol Buffers. השירות שלכם יספק:

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

שיטת ה-RPC הזו וסוגי ההודעות שלה יוגדרו בקובץ protos/route_guide.proto של קוד המקור שסופק.

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

סוגי הודעות

בקובץ protos/route_guide.proto של קוד המקור, מגדירים קודם את סוג ההודעה Point. הסמל Point מייצג זוג קואורדינטות של קו רוחב וקו אורך במפה. ב-codelab הזה, משתמשים במספרים שלמים לקואורדינטות:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

המספרים 1 ו-2 הם מספרי מזהים ייחודיים לכל אחד מהשדות במבנה message.

לאחר מכן, מגדירים את Feature סוג ההודעה. ‫Feature משתמש בשדה string כדי לציין את השם או הכתובת למשלוח דואר של משהו במיקום שצוין על ידי Point:

message Feature {
  // The name or address of the feature.
  string name = 1;

  // The point where the feature is located.
  Point location = 2;
}

שיטת השירות

לקובץ route_guide.proto יש מבנה service בשם RouteGuide שמגדיר שיטה אחת או יותר שסופקו על ידי השירות של האפליקציה.

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

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

זו שיטת RPC אוניטרית: RPC פשוט שבו הלקוח שולח בקשה לשרת ומחכה לתגובה, בדיוק כמו קריאה לפונקציה מקומית.

4. יצירת קוד הלקוח והשרת

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

יצרנו את grpcio-tools ליצירת קוד gRPC Python. היא כוללת:

  1. הקומפיילר הרגיל protoc שיוצר קוד Python מהגדרות message.
  2. תוסף gRPC protobuf שיוצר קוד Python (stub של לקוח ושרת) מההגדרות של service.

נתקין את חבילת Python‏ grpcio-tools באמצעות pip. כדאי ליצור סביבה וירטואלית של Python (venv) כדי לבודד את התלות של הפרויקט בחבילות המערכת:

python3 -m venv --upgrade-deps .venv

כדי להפעיל את הסביבה הווירטואלית במעטפת bash/zsh:

source .venv/bin/activate

במערכות Windows ובמעטפות לא סטנדרטיות, אפשר לעיין בטבלה שבכתובת https://docs.python.org/3/library/venv.html#how-venvs-work.

בשלב הבא, מתקינים את grpcio-tools (הפעולה הזו מתקינה גם את החבילה grpcio):

pip install grpcio-tools

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

python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto

הפעולה הזו תיצור את הקבצים הבאים לממשקים שהגדרנו ב-route_guide.proto:

  1. route_guide_pb2.py מכיל את הקוד שיוצר באופן דינמי כיתות שנוצרו מההגדרות של message.
  2. route_guide_pb2.pyi הוא קובץ stub או קובץ רמזים לגבי סוגים שנוצר מההגדרות של message. הוא מכיל רק את החתימות ללא הטמעה. סביבות פיתוח משולבות (IDE) יכולות להשתמש בקובצי stub כדי לספק השלמה אוטומטית טובה יותר וזיהוי שגיאות.
  3. route_guide_pb2_grpc.py נוצר מההגדרות של service ומכיל מחלקות ופונקציות ספציפיות ל-gRPC.

קוד ספציפי ל-gRPC מכיל:

  1. RouteGuideStub, שאפשר להשתמש בו בלקוח gRPC כדי להפעיל קריאות RPC של RouteGuide.
  2. RouteGuideServicer, שמגדיר את הממשק להטמעות של שירות RouteGuide.
  3. הפונקציה add_RouteGuideServicer_to_server שמשמשת לרישום RouteGuideServicer ב-gRPC server.

5. יצירת השירות

קודם נראה איך יוצרים RouteGuide שרת. יצירה והפעלה של שרת RouteGuide מתחלקות לשני פריטי עבודה:

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

אפשר למצוא את שרת RouteGuide הראשוני ב-start_here/route_guide_server.py.

הטמעה של RouteGuide

route_guide_server.py יש כיתה RouteGuideServicer שמהווה מחלקת משנה של הכיתה שנוצרה route_guide_pb2_grpc.RouteGuideServicer:

# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):

RouteGuideServicer מטמיע את כל שיטות השירות של RouteGuide.

נבחן עכשיו בפירוט הטמעה פשוטה של RPC. השיטה GetFeature מקבלת Point מהלקוח ומחזירה את פרטי התכונה התואמים מהמסד הנתונים שלה ב-Feature.

def GetFeature(self, request, context):
    feature = get_feature(self.db, request)
    if feature is None:
        return route_guide_pb2.Feature(name="", location=request)
    else:
        return feature

השיטה מקבלת route_guide_pb2.Pointבקשה לקריאה לשירות מרוחק (RPC)grpc.ServicerContext ואובייקט grpc.ServicerContext שמספק מידע ספציפי על ה-RPC, כמו מגבלות זמן קצוב לתפוגה. התשובה היא route_guide_pb2.Feature.

הפעלת השרת

אחרי שמטמיעים את כל השיטות של RouteGuide, השלב הבא הוא להפעיל שרת gRPC כדי שהלקוחות יוכלו להשתמש בשירות:

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
        RouteGuideServicer(),
        server,
    )
    listen_addr = "localhost:50051"
    server.add_insecure_port(listen_addr)
    print(f"Starting server on {listen_addr}")
    server.start()
    server.wait_for_termination()

השיטה של השרת start() לא חוסמת. ייווצר שרשור חדש כדי לטפל בבקשות. לרוב, לשרשור שקורא ל-server.start() לא תהיה עבודה אחרת לבצע בינתיים. במקרה כזה, אפשר להתקשר אל server.wait_for_termination() כדי לחסום את שרשור השיחות בצורה נקייה עד שהשרת יסיים את הפעולה.

6. יצירת הלקוח

בקטע הזה נראה איך ליצור לקוח לשירות RouteGuide. אפשר לראות את קוד הלקוח הראשוני בstart_here/route_guide_client.py.

יצירת דף stub

כדי לקרוא לשיטות של שירות, קודם צריך ליצור stub.

אנחנו יוצרים מופע של המחלקה RouteGuideStub מהמודול route_guide_pb2_grpc, שנוצר מ-.proto שלנו בתוך הקובץ route_guide_client.py.

channel = grpc.insecure_channel("localhost:50051")
stub = route_guide_pb2_grpc.RouteGuideStub(channel)

שיטות של שירותי שיחות

בשיטות RPC שמחזירות תגובה יחידה – שיטות שנקראות response-unary –‏ gRPC Python תומך בסמנטיקה של זרימת בקרה סינכרונית (חסימה) ואסינכרונית (לא חסימה).

Simple RPC

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

point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)

קריאה סינכרונית ל-RPC הפשוט GetFeature היא פשוטה כמעט כמו קריאה ל-method מקומית. הקריאה ל-RPC ממתינה לתגובת השרת, ותחזיר תגובה או תעלה חריגה. אפשר להתקשר לשיטה ולראות את התגובה כך:

feature = stub.GetFeature(point)
print(feature)

אפשר לבדוק את השדות של אובייקט התכונה ולהציג את תוצאת הבקשה:

if feature.name:
    print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
    print(f"Found no feature at at {format_point(feature.location)}")

7. רוצה לנסות?

מריצים את השרת:

python route_guide_server.py

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

python route_guide_client.py

יוצג פלט כמו זה, בלי חותמות זמן כדי שיהיה ברור:

name: "16 Old Brook Lane, Warwick, NY 10990, USA"
location {
  latitude: 412346009
  longitude: -744026814
}

Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814

8. המאמרים הבאים