עיבוד ברקע

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

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

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

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

  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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  5. התקינו את ה-CLI של Google Cloud.

  6. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  7. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  11. התקינו את ה-CLI של Google Cloud.

  12. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  13. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    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, בתרגום וב-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();
          }
        }
      }
    }
  • הפונקציה לטיפול באינדקס (/) מקבלת את כל התרגומים הקיימים מ-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. ה-handler של תרגום הבקשה, שרשום ב-/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 כדי לקבל את הטקסט לתרגום ואת שפת היעד הרצויה. לאחר מכן, ה-API של Translation מתרגם את המחרוזת לשפה שבחרתם.

    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

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