עיבוד ברקע

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

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

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

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

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

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

מטרות

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

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Cloud de Confiance by S3NS, והשימוש בהם כרוך בתשלום:

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

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

  1. In the Cloud de Confiance console, on the project selector page, select or create a Cloud de Confiance 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

  2. Verify that billing is enabled for your Cloud de Confiance project.

  3. Enable the Firestore, Pub/Sub, and Cloud Translation APIs.

    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

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

  5. הגדירו שה-CLI של gcloud ישתמש בזהות המאוחדת שלכם.

    איך נכנסים ל-CLI של gcloud באמצעות הזהות המאוחדת?

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

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

    מעבר למדריך ההגדרה של 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 במזהה הפרויקט ב- Cloud de Confiance .
  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 הוא שם הפרויקט שיצרתם.Cloud de Confiance הפקודה הזו מפיקה את נקודת הקצה שאליה מינוי Pub/Sub דוחף בקשות תרגום. חשוב לשמור את נקודת הקצה הזו, כי תצטרכו אותה כדי ליצור את המינוי ל-Pub/Sub, ותיכנסו לנקודת הקצה בדפדפן כדי לבקש תרגום חדש.

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

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

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

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

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

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

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

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

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

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

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

      כניסה לדף Cloud Run

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

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

הסרת המשאבים

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

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

    כניסה לדף Manage resources

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

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

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

    gcloud run services delete --region=$region background

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