Traitement en arrière-plan

De nombreuses applications doivent exécuter un traitement en arrière-plan dans d'autres contextes que celui d'une requête Web. Ce tutoriel indique comment créer une application Web qui permet aux utilisateurs de saisir du texte à traduire, puis qui affiche la liste des traductions précédentes. La traduction est effectuée dans un traitement en arrière-plan pour éviter de bloquer la requête de l'utilisateur.

Le schéma suivant illustre le processus de requête de traduction.

Schéma de l'architecture

Voici, dans l'ordre, comment fonctionne l'application du tutoriel :

  1. L'application accède à la page Web pour consulter la liste des traductions précédentes stockées dans Firestore.
  2. Elle demande la traduction d'un texte en saisissant un formulaire HTML.
  3. La requête de traduction est publiée dans Pub/Sub.
  4. Un service Cloud Run abonné à ce sujet Pub/Sub est déclenché.
  5. Le service Cloud Run utilise Cloud Translation pour traduire le texte.
  6. Le service Cloud Run stocke le résultat dans Firestore.

Ce tutoriel est destiné à toute personne intéressée par le traitement en arrière-plan avec Google Cloud. Il ne nécessite aucune connaissance particulière de Pub/Sub, Firestore ou Cloud Run. Cependant, pour comprendre l'intégralité du code, il peut être utile de se familiariser avec Java et HTML.

Objectifs

  • Comprendre et déployer un service Cloud Run
  • Tester l'application

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

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

    Enable the APIs

  5. Install the Google Cloud CLI.
  6. To initialize the gcloud CLI, run the following command:

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

    Go to project selector

  8. Make sure that billing is enabled for your Google Cloud project.

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

    Enable the APIs

  10. Install the Google Cloud CLI.
  11. To initialize the gcloud CLI, run the following command:

    gcloud init
  12. Mettez à jour les composants gcloud :
    gcloud components update
  13. Préparez votre environnement de développement.

    Accéder au guide de configuration Java

Préparer l'application

  1. Dans votre fenêtre de terminal, clonez le dépôt de l'exemple d'application sur votre machine locale :

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

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

  2. Accédez au répertoire contenant l'exemple de code de traitement en arrière-plan :

    cd getting-started-java/background

Comprendre l'application

L'application Web comporte deux composants principaux :

  • Un serveur HTTP Java pour gérer les requêtes Web. Il comporte les deux points de terminaison suivants :
    • /translate
      • GET (en utilisant un navigateur Web) : affiche les 10 dernières requêtes de traduction traitées soumises par les utilisateurs.
      • POST (avec un abonnement Pub/Sub) : traite les requêtes de traduction à l'aide de l'API Cloud Translation et enregistre les résultats dans Firestore.
    • /create : le formulaire pour soumettre de nouvelles requêtes de traduction.
  • Clients de service qui traitent les requêtes de traduction soumises par le formulaire Web. Trois clients travaillent ensemble :
    • Pub/Sub : lorsque le formulaire Web est soumis par un utilisateur, le client Pub/Sub publie un message avec les détails de la requête. Un abonnement créé dans ce tutoriel relaie ces messages vers le point de terminaison Cloud Run que vous créez pour effectuer des traductions.
    • Traduction : ce client gère les requêtes Pub/Sub en effectuant les traductions.
    • Firestore : une fois la traduction terminée, ce client stocke les données de la requête avec la traduction dans Firestore. Ce client lit également les requêtes les plus récentes sur le point de terminaison /translate principal.

Comprendre le code Cloud Run

  • L'application Cloud Run a des dépendances sur Firestore, Traduction et 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>
  • Les clients Firestore, Traduction et Pub/Sub globaux sont initialisés afin de pouvoir être réutilisés pour les appels. De cette façon, vous n'avez pas à initialiser de nouveaux clients à chaque appel, ce qui ralentirait l'exécution.

    @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();
          }
        }
      }
    }
  • Le gestionnaire d'index (/) récupère toutes les traductions existantes dans Firestore et renseigne un modèle HTML à l'aide de la liste suivante :

    @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);
    }
  • Pour demander de nouvelles traductions, envoyez un formulaire HTML. Le gestionnaire des requêtes de traduction, enregistré sur /create, analyse le formulaire envoyé, valide la requête, puis publie un message dans 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("/");
    }
  • L'abonnement Pub/Sub que vous créez transfère ces requêtes au point de terminaison Cloud Run, qui analyse le message Pub/Sub pour obtenir le texte à traduire et la langue cible souhaitée. L'API Traduction traduit ensuite la chaîne dans la langue que vous avez sélectionnée.

    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()));
  • L'application stocke les données de traduction dans un nouveau document qu'elle crée dans 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());

Déployer l'application Cloud Run

  1. Choisissez un nom de sujet Pub/Sub et générez un jeton de validation Pub/Sub en utilisant uuidgen ou un générateur UUID en ligne tel que uuidgenerator.net. Ce jeton garantit que le point de terminaison Cloud Run accepte uniquement les requêtes de l'abonnement Pub/Sub que vous créez.

    export PUBSUB_TOPIC=background-translate
    export PUBSUB_VERIFICATION_TOKEN=your-verification-token
  2. Créer un sujet Pub/Sub

     gcloud pubsub topics create $PUBSUB_TOPIC
    
    • Dans le fichier pom.xml, remplacez MY_PROJECT par votre ID de projet Cloud.
  3. Créez et déployez une image de votre code sur GCR (un dépôt d'images) avec le plugin Jib Maven.

     mvn clean package jib:build
    
  4. Déployez l'application sur 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 est le nom du projet Cloud que vous avez créé. Cette commande génère le point de terminaison auquel votre abonnement Pub/Sub transmet les requêtes de traduction. Notez la valeur de ce point de terminaison, car vous en aurez besoin pour créer l'abonnement Pub/Sub. Vous accéderez également à ce point de terminaison dans un navigateur pour demander une nouvelle traduction.

Tester l'application

Après avoir déployé le service Cloud Run, essayez de demander une traduction.

  1. Pour afficher l'application dans votre navigateur, accédez au point de terminaison Cloud Run que vous avez créé précédemment.

    Une page s'affiche, contenant une liste de traductions vide ainsi qu'un formulaire de requête de nouvelles traductions.

  2. Cliquez sur + Demander une traduction, remplissez le formulaire de demande, puis cliquez sur Envoyer.

  3. Après l'envoi, vous êtes ramené automatiquement au chemin /translate, mais la nouvelle traduction peut ne pas encore s'afficher. Pour actualiser la page, cliquez sur Actualiser. Une nouvelle ligne s'affiche dans la liste des traductions. Si vous ne voyez pas de traduction, attendez encore quelques secondes, puis réessayez. Si vous ne voyez toujours pas de traduction, consultez la section suivante sur le débogage de l'application.

Déboguer l'application

Si vous ne parvenez pas à vous connecter à votre service Cloud Run ou si vous ne voyez pas de nouvelles traductions, vérifiez les points suivants :

  • Vérifiez que la commande gcloud run deploy a bien abouti et n'a généré aucune erreur. Si des erreurs se sont produites (par exemple, message=Build failed), corrigez-les et réessayez.

  • Recherchez les erreurs dans les journaux :

    1. Dans Google Cloud Console, accédez à la page Cloud Run.

      Accéder à la page "Cloud Run"

    2. Cliquez sur le nom du service, background.

    3. Cliquez sur Logs (Journaux).

Effectuer un nettoyage

Supprimer le projet

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Supprimez les services Cloud Run.

  • Supprimez les services Cloud Run que vous avez créés dans ce tutoriel :

    gcloud run services delete --region=$region background

Étapes suivantes