הגדרת שירות gRPC

כדי ליצור שירות gRPC – בין אם אתם משתמשים ב-Cloud Endpoints ובין אם לא – אתם מציינים את הגדרת הממשק באחד או יותר קובצי proto, שהם קובצי טקסט עם הסיומת .proto. בקובץ proto מגדירים את הממשק של ה-API, כולל מבני הנתונים, השיטות, הפרמטרים של השיטות וסוגי ההחזרה. לאחר מכן, קומפלו את קובץ proto באמצעות מקמפל מאגר אחסון לפרוטוקולים ספציפי לשפה, protoc. מידע נוסף זמין במאמרים מה זה gRPC? ומושגים של gRPC.

כדי ש-Endpoints ינהל את שירות ה-gRPC, בנוסף לקובץ ה-proto המהודר, צריך לציין הגדרת שירות בקובץ YAML אחד או יותר. הגדרת שירות היא מפרט שמאפשר להגדיר את ההתנהגות של שירות gRPC, כולל אימות, מכסות ועוד.

סקירה כללית על הגדרת שירות

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

לדוגמה:

type: google.api.Service
config_version: 3
name: calendar.googleapis.com
title: Google Calendar API
apis:
- name: google.calendar.v3.Calendar
authentication:
  providers:
  - id: google_calendar_auth
    jwks_uri: https://www.googleapis.com/oauth2/v1/certs
    issuer: https://securetoken.google.com
  rules:
  - selector: "*"
    requirements:
      provider_id: google_calendar_auth
backend:
  rules:
    - selector: "*"
      address: grpcs://my-service-98sdf8sd0-uc.a.run.app

כל קטע משנה בדרך כלל תואם להודעת .proto שבה מגדירים היבטים שונים של השירות. לדוגמה:

  • authentication: מציין איך מתבצע אימות של המתקשרים.
  • backend: שולט בניתוב של ה-backend המרוחק.
  • http: מגדיר את הכללים למיפוי של method ל-RPC ל-method אחת או יותר של API בארכיטקטורת REST.
  • usage: מאפשר להפעיל ולהשבית אימות של מפתח API באופן סלקטיבי.

הסכימה הרשמית של הגדרת השירות מוגדרת על ידי ההודעה .proto google.api.Service.

הגדרה בסיסית

הדוגמה של חנות הספרים, שבה נעשה שימוש במדריכים של gRPC, כוללת קובץ בסיסי של הגדרת ממשק וקובץ של הגדרת שירות.

הגדרת הממשק של חנות הספרים, bookstore.proto:

syntax = "proto3";

package endpoints.examples.bookstore;

option java_multiple_files = true;
option java_outer_classname = "BookstoreProto";
option java_package = "com.google.endpoints.examples.bookstore";

import "google/protobuf/empty.proto";

service Bookstore {
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {}
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {}
  rpc GetShelf(GetShelfRequest) returns (Shelf) {}
  rpc DeleteShelf(DeleteShelfRequest) returns (google.protobuf.Empty) {}
  rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {}
  rpc CreateBook(CreateBookRequest) returns (Book) {}
  rpc GetBook(GetBookRequest) returns (Book) {}
  rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {}
}

message Shelf {
  int64 id = 1;
  string theme = 2;
}

message Book {
  int64 id = 1;
  string author = 2;
  string title = 3;
}

הגדרת השירות של חנות הספרים, api_config.yaml:

type: google.api.Service
config_version: 3

name: bookstore.endpoints.MY_PROJECT_ID.cloud.goog

title: Bookstore gRPC API
apis:
- name: endpoints.examples.bookstore.Bookstore

דוגמת הקוד הקודמת היא הגדרת השירות הפשוטה ביותר, כי:

  • הגדרת שירות בשם bookstore.endpoints.MY_PROJECT_ID.cloud.goog, כאשר MY_PROJECT_ID מייצג את מזהה הפרויקט ב- Google Cloud .
  • ההגדרה הזו מציינת שהשירות חושף את ה-API‏ endpoints.examples.bookstore.Bookstore, כפי שמוגדר בקובץ bookstore.proto.
  • מציינים שם תיאורי שמופיע בדף Endpoints > Services במסוף Google Cloud אחרי שמפעילים את ההגדרה. כדי לראות הערות מפורטות יותר, אפשר לעיין בגרסאות המלאות של כל קובץ ב-GitHub.

כללים ובוררים

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

בורר הוא רשימה מופרדת בפסיקים של שמות מלאים שמוגדרים בשירות: שיטה, הודעה, שדה, enum או ערכי enum. הפלח האחרון של השם יכול להיות התו הכללי לחיפוש *, שתואם לכל סיומת. אפשר להשתמש בתווים כלליים רק בסוף השם ורק עבור קטע שלם בשם. לדוגמה: foo.* תקין, אבל foo.b* או foo.*.bar לא תקינים. כדי להגדיר ברירת מחדל לכל הרכיבים הרלוונטיים, מציינים רק *.

דוגמה 1 (משפיעה על כל השירות):

usage:
  rules:
  # Allow unregistered calls for all methods.
  - selector: "*"
    allow_unregistered_calls: true

דוגמה 2 (משפיעה על שיטה אחת):

usage:
  rules: # IMPORTANT: ONLY LAST MATCHING RULE IS APPLIED
  # Disable API key validation on just the ListShelves method
  # while requiring an API key for the rest of the API.
  - selector: "*"
    allow_unregistered_calls: false
  - selector: "endpoints.examples.bookstore.BookStore.ListShelves"
    allow_unregistered_calls: true

בדוגמה הקודמת אפשר לראות איך להגדיר אימות של מפתח API לכל ה-methods, חוץ מ-method ‏ListShelves.

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

שימוש בכמה קובצי YAML

יכול להיות שיהיה לכם שימושי להשתמש ביותר מקובץ YAML אחד כדי להגדיר תכונות שונות לאותו שירות. שימוש בכמה קובצי YAML מקל על השימוש החוזר בקבצים ועל שינוי שלהם עבור סביבות שונות. לדוגמה, בדוגמה של חנות הספרים, ההגדרה הבסיסית מצוינת בקובץ api_config.yaml, והכללים של HTTP מצוינים בקובץ api_config_http.yaml. כך תוכלו לפרוס את כללי ה-HTTP רק אם אתם רוצים להפעיל המרת קידוד מ-JSON/HTTP ל-gRPC עבור חנות הספרים.

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

בדומה לכללים, המיזוג תלוי בסדר. אם יש שתי הגדרות שירות, הגדרת השירות השנייה מבטלת את הראשונה.

הערות (כללי HTTP בלבד)

במקום להשתמש בקובץ YAML כדי להגדיר אפשרויות HTTP, אפשר להגדיר אותן ישירות בקובץ proto באמצעות מנגנון האפשרויות. ההערות ל-API מוגדרות בקובץ annotations.proto.

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

אם אפשרות הגדרה מסופקת גם בקובץ proto וגם בקובץ ה-YAML של הגדרת השירות, הגדרת השירות מבטלת את ההערה.

הערה לדוגמה בקובץ proto:

rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
    option (google.api.http) = { get: "/v1/shelves" };
}


# The preceding annotation is equivalent to the following service configuration:

http:
  rules:
  - selector: endpoints.examples.bookstore.BookStore.ListShelves
    get: '/v1/shelves'

מידע נוסף אפשר למצוא במאמר בנושא המרת קידוד HTTP/JSON ל-gRPC.

אימות ההגדרות

כדי ליצור את הגדרות זמן הריצה של השירות, מבצעים פריסה של הגדרות השירות ושל קובצי proto שעברו הידור באמצעות gcloud endpoints services deploy. הפקודה gcloud endpoints services deploy מאמתת את הגדרת השירות ומסמנת שגיאות ואזהרות.

האזהרות מחולקות לאזהרות רגילות ולאזהרות של כלי Linter. אזהרות של Linter משתמשות במזהה מהצורה <group>-<rule>, כאשר <group> מציין את קבוצת ההגדרות ו-<rule> את כלל ה-Linter הספציפי. לדוגמה, אם מתחת לקבוצה מופיע versioning והכלל הוא http-version-prefix:

WARNING: bookstore.proto:51:3: (lint) versioning-http-version-prefix: method
'endpoints.examples.bookstore.BookStore.ListShelves' has a different version
prefix in HTTP path ('v2') than api version 'v1'.

אפשר להשבית אזהרות של כלי לבדיקת קוד על ידי הוספת הנחיה לתיעוד של רכיב API. אפשר להוסיף את ההנחיה לקובץ proto או לקובץ ה-YAML של תצורת השירות. לדוגמה, הפקודה הבאה מבטלת את האזהרה הקודמת:

service Bookstore {
  // Returns an object.
  // (== suppress_warning versioning-http-version-prefix ==)
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
      option (google.api.http) = { get: "/v1/shelves" };
  }
}

כדי להשבית אזהרות לקבוצה שלמה, אפשר להשתמש בתו כללי (*) במקום בשמות של כללים. לדוגמה, versioning-* משבית את כל האזהרות שקשורות לקבוצה versioning.

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

// (== suppress_warning versioning-* ==)
service Bookstore {
  // Returns an object.
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
      option (google.api.http) = { get: "/v1/shelves" };
  }
}

כדי להשבית אזהרה באופן גלובלי, צריך לצרף אותה לתיעוד של סקירת הגדרת השירות:

type: google.api.Service
name: your_api.endpoints.<producer-project-id>.cloud.goog
title: Bookstore gRPC API
apis:
- name: endpoints.examples.bookstore.BookStore
documentation:
    overview: |
      A simple Bookstore gRPC API.
      (== suppress_warning documentation-presence ==)

שימו לב שהוראות בתיעוד כמו suppress_warning חייבות להופיע בשורה נפרדת, אחרת המערכת לא תזהה אותן. סמני מילוליות של בלוקים ב-YAML, כמו >, מסירים את כל השורות החדשות, ולכן אם משתמשים ב-overview: > בדוגמה הקודמת, ההסתרה לא פועלת. מידע נוסף על בלוקים מילוליים ב-YAML זמין במאמר בנושא תיחום עם הזחה.

המאמרים הבאים