עיבוד ברקע

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

התרשים הבא מדגים את תהליך בקשת התרגום.

דיאגרמה של הארכיטקטורה.

כך פועלת אפליקציית המדריך:

  1. עוברים לדף האינטרנט כדי לראות רשימה של תרגומים קודמים, שמאוחסנים ב-Firestore.
  2. שליחת בקשה לתרגום טקסט באמצעות הזנת טופס HTML.
  3. בקשת התרגום מתפרסמת ב-Pub/Sub.
  4. מופעל שירות Cloud Run שנרשם לנושא Pub/Sub.
  5. השירות Cloud Run משתמש ב-Cloud Translation כדי לתרגם את הטקסט.
  6. שירות Cloud Run שומר את התוצאה ב-Firestore.

המדריך הזה מיועד לכל מי שרוצה ללמוד על עיבוד ברקע באמצעות Google Cloud. לא נדרש ניסיון קודם ב-Pub/Sub, ב-Firestore או ב-Cloud Run. עם זאת, כדי להבין את כל הקוד, כדאי שיהיה לכם ניסיון מסוים ב-Java וב-HTML.

מטרות

  • הסבר על שירות Cloud Run ופריסה שלו.
  • אפשר לנסות את האפליקציה.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

בעזרת מחשבון העלויות אתם יכולים להעריך את העלויות בהתאם לשימוש הצפוי.

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

לפני שמתחילים

  1. נכנסים לחשבון Google Cloud . אנחנו ממליצים למשתמשים חדשים ב- Google Cloud ליצור חשבון כדי שיוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Firestore, Pub/Sub, and Cloud Translation APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  5. Install the Google Cloud CLI.

  6. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  9. Verify that billing is enabled for your Google Cloud project.

  10. Enable the Firestore, Pub/Sub, and Cloud Translation APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  11. Install the Google Cloud CLI.

  12. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  13. To initialize the gcloud CLI, run the following command:

    gcloud init
  14. מעדכנים את הרכיבים של gcloud:
    gcloud components update
  15. מכינים את סביבת הפיתוח.

    למדריך ההגדרה של Java

הכנת האפליקציה

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

    git clone https://github.com/GoogleCloudPlatform/getting-started-java.git

    אפשר גם להוריד את הדוגמה כקובץ ZIP ולחלץ אותו.

  2. עוברים לספרייה שמכילה את הקוד לדוגמה של עיבוד ברקע:

    cd getting-started-java/background

הסבר על האפליקציה

אפליקציית האינטרנט מורכבת משני רכיבים עיקריים:

  • שרת HTTP של Java לטיפול בבקשות לאחזור מהרשת. לשרת יש שתי נקודות קצה:
    • /translate
      • ‫GET (באמצעות דפדפן אינטרנט): מוצגות 10 בקשות התרגום האחרונות שמשתמשים שלחו ועברו עיבוד.
      • ‫POST (עם מינוי ל-Pub/Sub): מעבדת בקשות תרגום באמצעות Cloud Translation API ושומרת את התוצאות ב-Firestore.
    • ‫/create: הטופס לשליחת בקשות תרגום חדשות.
  • לקוחות שירות שמעבדים את בקשות התרגום שנשלחות דרך הטופס באינטרנט. יש שלושה לקוחות שעובדים ביחד:
    • ‫Pub/Sub: כשהמשתמש שולח את הטופס באינטרנט, לקוח Pub/Sub מפרסם הודעה עם פרטי הבקשה. מינוי שנוצר במדריך הזה מעביר את ההודעות האלה לנקודת הקצה של Cloud Run שיוצרים כדי לבצע תרגומים.
    • תרגום: הלקוח הזה מטפל בבקשות Pub/Sub על ידי ביצוע התרגומים.
    • ‫Firestore: כשהתרגום מסתיים, הלקוח הזה מאחסן את נתוני הבקשה יחד עם התרגום ב-Firestore. הלקוח הזה קורא גם את הבקשות האחרונות בנקודת הקצה הראשית /translate.

הסבר על הקוד של Cloud Run

  • לאפליקציה ב-Cloud Run יש תלות ב-Firestore,‏ Translation ו-Pub/Sub.

    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-firestore</artifactId>
      <version>3.13.2</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-translate</artifactId>
      <version>2.20.0</version>
    </dependency>
    
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-pubsub</artifactId>
      <version>1.123.17</version>
    </dependency>
  • הלקוחות הגלובליים של Firestore,‏ Translation ו-Pub/Sub מאותחלים כדי שאפשר יהיה להשתמש בהם מחדש בין הפעלות. כך לא תצטרכו לאתחל לקוחות חדשים בכל הפעלה, מה שיאט את הביצוע.

    @WebListener("Creates Firestore and TranslateServlet service clients for reuse between requests.")
    public class BackgroundContextListener implements ServletContextListener {
      @Override
      public void contextDestroyed(javax.servlet.ServletContextEvent event) {}
    
      @Override
      public void contextInitialized(ServletContextEvent event) {
        String firestoreProjectId = System.getenv("FIRESTORE_CLOUD_PROJECT");
        Firestore firestore = (Firestore) event.getServletContext().getAttribute("firestore");
        if (firestore == null) {
          firestore =
              FirestoreOptions.getDefaultInstance().toBuilder()
                  .setProjectId(firestoreProjectId)
                  .build()
                  .getService();
          event.getServletContext().setAttribute("firestore", firestore);
        }
    
        Translate translate = (Translate) event.getServletContext().getAttribute("translate");
        if (translate == null) {
          translate = TranslateOptions.getDefaultInstance().getService();
          event.getServletContext().setAttribute("translate", translate);
        }
    
        String topicId = System.getenv("PUBSUB_TOPIC");
        TopicName topicName = TopicName.of(firestoreProjectId, topicId);
        Publisher publisher = (Publisher) event.getServletContext().getAttribute("publisher");
        if (publisher == null) {
          try {
            publisher = Publisher.newBuilder(topicName).build();
            event.getServletContext().setAttribute("publisher", publisher);
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    }
  • ה-handler של האינדקס (/) מקבל את כל התרגומים הקיימים מ-Firestore וממלא תבנית HTML ברשימה:

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");
      CollectionReference translations = firestore.collection("translations");
      QuerySnapshot snapshot;
      try {
        snapshot = translations.limit(10).get().get();
      } catch (InterruptedException | ExecutionException e) {
        throw new ServletException("Exception retrieving documents from Firestore.", e);
      }
      List<TranslateMessage> translateMessages = Lists.newArrayList();
      List<QueryDocumentSnapshot> documents = Lists.newArrayList(snapshot.getDocuments());
      documents.sort(Comparator.comparing(DocumentSnapshot::getCreateTime));
    
      for (DocumentSnapshot document : Lists.reverse(documents)) {
        String encoded = gson.toJson(document.getData());
        TranslateMessage message = gson.fromJson(encoded, TranslateMessage.class);
        message.setData(decode(message.getData()));
        translateMessages.add(message);
      }
      req.setAttribute("messages", translateMessages);
      req.setAttribute("page", "list");
      req.getRequestDispatcher("/base.jsp").forward(req, resp);
    }
  • כדי לבקש תרגומים חדשים, שולחים טופס HTML. הבקשה מטפלת בתרגום, רשומה ב-/create, מנתחת את הטופס שנשלח, מאמתת את הבקשה ומפרסמת הודעה ב-Pub/Sub:

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      String text = req.getParameter("data");
      String sourceLang = req.getParameter("sourceLang");
      String targetLang = req.getParameter("targetLang");
    
      Enumeration<String> paramNames = req.getParameterNames();
      while (paramNames.hasMoreElements()) {
        String paramName = paramNames.nextElement();
        logger.warning("Param name: " + paramName + " = " + req.getParameter(paramName));
      }
    
      Publisher publisher = (Publisher) getServletContext().getAttribute("publisher");
    
      PubsubMessage pubsubMessage =
          PubsubMessage.newBuilder()
              .setData(ByteString.copyFromUtf8(text))
              .putAttributes("sourceLang", sourceLang)
              .putAttributes("targetLang", targetLang)
              .build();
    
      try {
        publisher.publish(pubsubMessage).get();
      } catch (InterruptedException | ExecutionException e) {
        throw new ServletException("Exception publishing message to topic.", e);
      }
    
      resp.sendRedirect("/");
    }
  • המינוי ל-Pub/Sub שאתם יוצרים מעביר את הבקשות האלה לנקודת הקצה של Cloud Run, שמנתחת את ההודעה של Pub/Sub כדי לקבל את הטקסט לתרגום ואת שפת היעד הרצויה. לאחר מכן, Translation API מתרגם את המחרוזת לשפה שבחרתם.

    String body = req.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
    
    PubSubMessage pubsubMessage = gson.fromJson(body, PubSubMessage.class);
    TranslateMessage message = pubsubMessage.getMessage();
    
    // Use Translate service client to translate the message.
    Translate translate = (Translate) this.getServletContext().getAttribute("translate");
    message.setData(decode(message.getData()));
    Translation translation =
        translate.translate(
            message.getData(),
            Translate.TranslateOption.sourceLanguage(message.getAttributes().getSourceLang()),
            Translate.TranslateOption.targetLanguage(message.getAttributes().getTargetLang()));
  • האפליקציה מאחסנת את נתוני התרגום במסמך חדש שהיא יוצרת ב-Firestore.

    // Use Firestore service client to store the translation in Firestore.
    Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");
    
    CollectionReference translations = firestore.collection("translations");
    
    ApiFuture<WriteResult> setFuture = translations.document().set(message, SetOptions.merge());
    
    setFuture.get();
    resp.getWriter().write(translation.getTranslatedText());

פריסת האפליקציה של Cloud Run

  1. בוחרים שם לנושא Pub/Sub ויוצרים אסימון אימות של Pub/Sub באמצעות uuidgen או מחולל UUID אונליין כמו uuidgenerator.net. האסימון הזה יבטיח שנקודת הקצה של Cloud Run תקבל רק בקשות מהמינוי ל-Pub/Sub שתיצרו.

    export PUBSUB_TOPIC=background-translate
    export PUBSUB_VERIFICATION_TOKEN=your-verification-token
  2. יוצרים נושא Pub/Sub:

     gcloud pubsub topics create $PUBSUB_TOPIC
    
    • מחליפים את MY_PROJECT בקובץ pom.xml במזהה הפרויקט ב- Google Cloud .
  3. יוצרים קובץ אימג' של הקוד ופורסים אותו ב-GCR (מאגר קובצי אימג') באמצעות הפלאגין Jib Maven.

     mvn clean package jib:build
    
  4. פורסים את האפליקציה ב-Cloud Run:

    gcloud run deploy background --image gcr.io/MY_PROJECT/background \
          --platform managed --region us-central1 --memory 512M \
          --update-env-vars PUBSUB_TOPIC=$PUBSUB_TOPIC,PUBSUB_VERIFICATION_TOKEN=$PUBSUB_VERIFICATION_TOKEN

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

בדיקת האפליקציה

אחרי שפורסים את שירות Cloud Run, אפשר לנסות לבקש תרגום.

  1. כדי להציג את האפליקציה בדפדפן, עוברים לנקודת הקצה של Cloud Run שיצרתם קודם.

    יש דף עם רשימה ריקה של תרגומים וטופס לבקשת תרגומים חדשים.

  2. לוחצים על + בקשה לתרגום, ממלאים את טופס הבקשה ולוחצים על שליחה.

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

ניפוי באגים באפליקציה

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

  • בודקים שהפקודה gcloud run deploy הושלמה בהצלחה ושלא הוחזרו שגיאות. אם היו שגיאות (לדוגמה, message=Build failed), תקנו אותן ונסו להריץ שוב.

  • בודקים אם יש שגיאות ביומנים:

    1. נכנסים לדף Cloud Run במסוף Google Cloud .

      כניסה לדף Cloud Run

    2. לוחצים על שם השירות, background.

    3. לוחצים על יומנים.

הסרת המשאבים

מחיקת הפרויקט

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

מחיקת שירותי Cloud Run.

  • מוחקים את שירותי Cloud Run שיצרתם במדריך הזה:

    gcloud run services delete --region=$region background

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