Hintergrundverarbeitung

Viele Anwendungen müssen eine Hintergrundverarbeitung außerhalb des Kontexts einer Webanfrage ausführen. In dieser Anleitung wird eine Webanwendung erstellt, mit der Nutzer Text eingeben können, der übersetzt werden soll. Anschließend wird eine Liste früherer Übersetzungen angezeigt. Die Übersetzung erfolgt in einem Hintergrundprozess, um die Anfrage des Nutzers nicht zu blockieren.

Das folgende Diagramm veranschaulicht den Ablauf der Übersetzungsanfrage.

Diagramm der Architektur

Wie die in dieser Anleitung verwendete Anwendung funktioniert, sehen Sie an der Abfolge der Ereignisse:

  1. Sie rufen die Webseite auf, um sich eine Liste früherer, in Firestone gespeicherter Übersetzungen anzeigen zu lassen.
  2. Sie fordern die Übersetzung eines Textes an. Dazu füllen Sie ein HTML-Formular aus.
  3. Die Übersetzungsanfrage wird in Pub/Sub veröffentlicht.
  4. Ein Cloud Run-Dienst, der dieses Pub/Sub-Thema abonniert hat, wird ausgelöst.
  5. Der Cloud Run-Dienst übersetzt den Text mit Cloud Translation.
  6. Der Cloud Run-Dienst speichert das Ergebnis in Firestore.

Diese Anleitung richtet sich an alle, die mehr über die Hintergrundverarbeitung mit Google Cloud erfahren möchten. Es sind keine Vorkenntnisse in Pub/Sub, Firestore oder Cloud Run erforderlich. Um den gesamten Code zu verstehen, ist allerdings etwas Erfahrung mit Java und HTML hilfreich.

Lernziele

  • Cloud Run-Dienst verstehen und bereitstellen
  • Anwendung testen

Kosten

In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss der in diesem Dokument beschriebenen Aufgaben können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  4. Firestore, Pub/Sub, and Cloud Translation APIs aktivieren.

    Aktivieren Sie die APIs

  5. Installieren Sie die Google Cloud CLI.
  6. Führen Sie folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  7. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  8. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  9. Firestore, Pub/Sub, and Cloud Translation APIs aktivieren.

    Aktivieren Sie die APIs

  10. Installieren Sie die Google Cloud CLI.
  11. Führen Sie folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  12. Aktualisieren Sie die gcloud-Komponenten:
    gcloud components update
  13. Bereiten Sie die Entwicklungsumgebung vor.

    Einrichtungsleitfaden für Java aufrufen

Anwendung vorbereiten

  1. Klonen Sie das Beispiel-App-Repository in Ihrem Terminalfenster auf Ihren lokalen Computer:

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

    Sie können auch das Beispiel als ZIP-Datei herunterladen und extrahieren.

  2. Wechseln Sie in das Verzeichnis, das den Beispielcode für die Hintergrundverarbeitung enthält:

    cd getting-started-java/background

Informationen zur Anwendung

Zur Webanwendung gehören zwei Hauptkomponenten:

  • Ein Java-HTTP-Server für Webanfragen. Der Server hat die folgenden beiden Endpunkte:
    • /translate
      • GET (mithilfe eines Webbrowsers): Zeigt die 10 zuletzt verarbeiteten Übersetzungsanfragen an, die von Nutzern gesendet wurden.
      • POST (mit einem Pub/Sub-Abo): Verarbeitet Übersetzungsanfragen mithilfe der Cloud Translation API und speichert die Ergebnisse in Firestore.
    • /create: Das Formular zum Senden neuer Übersetzungsanfragen.
  • Dienst-Clients, die die vom Webformular übermittelten Übersetzungsanfragen verarbeiten. Es gibt drei Clients, die zusammenarbeiten:
    • Pub/Sub: Wenn das Webformular von einem Nutzer gesendet wird, veröffentlicht der Pub/Sub-Client eine Nachricht mit den Anfragedetails. Ein in diesem Lernprogramm erstelltes Abo leitet diese Nachrichten an den Cloud Run-Endpunkt weiter, den Sie zum Ausführen von Übersetzungen erstellen.
    • Übersetzung: Dieser Client verarbeitet Pub/Sub-Anfragen durch Ausführen der Übersetzungen.
    • Firestore: Wenn die Übersetzung abgeschlossen ist, speichert dieser Client die Anfragedaten zusammen mit der Übersetzung in Firestore. Dieser Client liest auch die neuesten Anfragen auf dem Hauptendpunkt /translate.

Informationen zum Cloud Run-Code

  • Die Cloud Run-Anwendung ist abhängig von Firestore, Translation und Pub/Sub.

    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-firestore</artifactId>
      <version>3.0.16</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-translate</artifactId>
      <version>2.1.1</version>
    </dependency>
    
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-pubsub</artifactId>
      <version>1.113.7</version>
    </dependency>
  • Die globalen Firestore-, Übersetzer- und Pub/Sub-Clients werden initialisiert, damit sie zwischen Aufrufen wiederverwendet werden können. Auf diese Weise müssen Sie nicht bei jedem Aufruf neue Clients initialisieren, was die Ausführung verlangsamen würde.

    @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();
          }
        }
      }
    }
  • Der Index-Handler (/) ruft alle vorhandenen Übersetzungen aus Firestore ab und füllt eine HTML-Vorlage mit der Liste aus:

    @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);
    }
  • Neue Übersetzungen werden durch Senden eines HTML-Formulars angefordert. Der Handler für Übersetzungsanfragen, der unter /create registriert ist, parst das gesendete Formular, validiert die Anfrage und veröffentlicht eine Nachricht in 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("/");
    }
  • Das von Ihnen erstellte Pub/Sub-Abo leitet diese Anfragen an den Cloud Run-Endpunkt weiter, der die Pub/Sub-Nachricht analysiert, um den zu übersetzenden Text und die gewünschte Zielsprache zu erhalten. Die Translation API übersetzt den String in die von Ihnen ausgewählte Sprache.

    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()));
  • Die Anwendung speichert die Übersetzungsdaten in einem neuen Dokument, das sie in Firestore erstellt.

    // 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-App bereitstellen

  1. Wählen Sie einen Pub/Sub-Topic-Namen und generieren Sie ein Pub/Sub-Verifikations-Token mit uuidgen oder einem Online-UUID-Generator wie uuidgenerator.net. Dieses Token stellt sicher, dass der Cloud Run-Endpunkt nur Anfragen aus dem von Ihnen erstellten Pub/Sub-Abo akzeptiert.

    export PUBSUB_TOPIC=background-translate
    export PUBSUB_VERIFICATION_TOKEN=your-verification-token
    
  2. Pub/Sub-Thema erstellen:

     gcloud pubsub topics create $PUBSUB_TOPIC
    
    • Ersetzen Sie MY_PROJECT in der Datei pom.xml durch Ihre Cloud-Projekt-ID.
  3. Erstellen Sie mit dem Maven-Plug-in Jib ein Image Ihres Codes und stellen Sie es in GCR (Image-Repository) bereit.

     mvn clean package jib:build
    
  4. Stellen Sie die Anwendung für Cloud Run bereit:

    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
    

    Dabei ist MY_PROJECT der Name des von Ihnen erstellten Cloud-Projekts. Dieser Befehl gibt den Endpunkt aus, an den Ihr Pub/Sub-Abo Übersetzungsanfragen sendet. Notieren Sie sich diesen Endpunkt, da Sie ihn zum Erstellen des Pub/Sub-Abos benötigen und Sie den Endpunkt in einem Browser aufrufen, um eine neue Übersetzung anzufordern.

Anwendung testen

Nachdem Sie den Cloud Run-Dienst bereitgestellt haben, versuchen Sie, eine Übersetzung anzufordern.

  1. Um die Anwendung in Ihrem Browser anzuzeigen, rufen Sie den zuvor erstellten Cloud Run-Endpunkt auf.

    Sie sehen eine Seite mit einer leeren Liste von Übersetzungen und einem Formular zum Anfordern neuer Übersetzungen.

  2. Klicken Sie auf + Übersetzung anfordern, füllen Sie das Anfrageformular aus und klicken Sie dann auf Senden.

  3. Dadurch werden Sie automatisch zum /translate-Pfad zurückgeleitet, die neue Übersetzung wird möglicherweise noch nicht angezeigt. Klicken Sie zum Aktualisieren der Seite auf Aktualisieren . Die Übersetzungsliste enthält jetzt eine neue Zeile. Wenn Sie keine Übersetzung sehen, warten Sie einige Sekunden und versuchen es dann noch einmal. Ist immer noch keine Übersetzung zu sehen, lesen Sie den nächsten Abschnitt zum Debugging der Anwendung.

Fehler in der Anwendung beheben

Wenn Sie keine Verbindung zu Ihrem Cloud Run-Dienst herstellen können oder keine neuen Übersetzungen sehen, prüfen Sie Folgendes:

  • Prüfen Sie, ob der Befehl gcloud run deploy erfolgreich ausgeführt wurde und keine Fehler ausgegeben hat. Wenn Fehler aufgetreten sind (z. B. message=Build failed), beheben Sie diese und versuchen Sie es noch einmal.

  • Prüfen Sie die Logs auf Fehler:

    1. Rufen Sie in der Google Cloud Console die Seite „Cloud Run“ auf.

      Zur Cloud Run-Seite

    2. Klicken Sie auf den Dienstnamen, background.

    3. Klicken Sie auf Logs.

Bereinigen

Projekt löschen

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Cloud Run-Dienste löschen.

  • Löschen Sie die Cloud Run-Dienste, die Sie in dieser Anleitung erstellt haben:

    gcloud run services delete --region=$region background

Weitere Informationen