Traitement en arrière-plan avec Go


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, les étapes du fonctionnement de 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. Une fonction Cloud Run abonnée à ce sujet Pub/Sub est déclenchée.
  5. La fonction Cloud Run utilise Cloud Translation pour traduire le texte.
  6. La fonction 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. Aucune expérience antérieure n’est requise avec Pub/Sub, Firestore, App Engine Fonctions Cloud Run. Cependant, pour comprendre l'intégralité du code, il peut être utile de se familiariser avec Go, JavaScript et HTML.

Objectifs

  • Comprendre et déployer une fonction Cloud Run
  • Comprendre et déployer une application App Engine
  • 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, Cloud Run functions, Pub/Sub, and Cloud Translation APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  7. Enable the Firestore, Cloud Run functions, Pub/Sub, and Cloud Translation APIs.

    Enable the APIs

  8. Dans la console Google Cloud, ouvrez l'application dans Cloud Shell.

    Accéder à Cloud Shell

    Cloud Shell vous permet d'accéder en ligne de commande à vos ressources cloud, directement depuis votre navigateur. Ouvrez Cloud Shell dans votre navigateur et cliquez sur Proceed (Continuer) pour télécharger l'exemple de code et le modifier dans le répertoire de l'application.

  9. Dans Cloud Shell, configurez l'outil gcloud pour qu'il utilise votre projet Google Cloud :
    # Configure gcloud for your project
    gcloud config set project YOUR_PROJECT_ID

Comprendre la fonction Cloud Run

  • La fonction commence par importer plusieurs dépendances telles que Firestore et Translation. Elle comporte également des variables et des types globaux.
    
    // Package background contains a Cloud Function to translate text.
    // The function listens to Pub/Sub, does the translations, and stores the
    // result in Firestore.
    package background
    
    import (
    	"context"
    	"crypto/sha512"
    	"encoding/base64"
    	"encoding/json"
    	"fmt"
    	"os"
    	"strings"
    
    	"cloud.google.com/go/firestore"
    	"cloud.google.com/go/translate"
    	"golang.org/x/text/language"
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/status"
    )
    
    // A Translation contains the original and translated text.
    type Translation struct {
    	Original         string `json:"original"`
    	Translated       string `json:"translated"`
    	OriginalLanguage string `json:"original_language"`
    	Language         string `json:"language"`
    }
    
    // Clients reused between function invocations.
    var (
    	translateClient *translate.Client
    	firestoreClient *firestore.Client
    )
    
    // PubSubMessage is the payload of a Pub/Sub event.
    // See https://cloud.google.com/functions/docs/calling/pubsub.
    type PubSubMessage struct {
    	Data []byte `json:"data"`
    }
    
  • Les clients Firestore et Translation internationaux sont initialisés afin qu'ils puissent être réutilisés pour les appels de fonction. De cette manière, vous n'avez pas besoin d'initialiser de nouveaux clients à chaque appel de fonction, ce qui ralentirait l'exécution.
    
    // initializeClients creates translateClient and firestoreClient if they haven't
    // been created yet.
    func initializeClients() error {
    	projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
    	if projectID == "" {
    		return fmt.Errorf("GOOGLE_CLOUD_PROJECT must be set")
    	}
    
    	if translateClient == nil {
    		// Pre-declare err to avoid shadowing translateClient.
    		var err error
    		// Use context.Background() so the client can be reused.
    		translateClient, err = translate.NewClient(context.Background())
    		if err != nil {
    			return fmt.Errorf("translate.NewClient: %w", err)
    		}
    	}
    	if firestoreClient == nil {
    		// Pre-declare err to avoid shadowing firestoreClient.
    		var err error
    		// Use context.Background() so the client can be reused.
    		firestoreClient, err = firestore.NewClient(context.Background(), projectID)
    		if err != nil {
    			return fmt.Errorf("firestore.NewClient: %w", err)
    		}
    	}
    	return nil
    }
    
  • L'API Translation traduit la chaîne la langue sélectionnée.
    
    // translateString translates text to lang, returning:
    // * the translated text,
    // * the automatically detected source language, and
    // * an error.
    func translateString(ctx context.Context, text string, lang string) (translated string, originalLang string, err error) {
    	l, err := language.Parse(lang)
    	if err != nil {
    		return "", "", fmt.Errorf("language.Parse: %w", err)
    	}
    
    	outs, err := translateClient.Translate(ctx, []string{text}, l, nil)
    	if err != nil {
    		return "", "", fmt.Errorf("Translate: %w", err)
    	}
    
    	if len(outs) < 1 {
    		return "", "", fmt.Errorf("Translate got %d translations, need at least 1", len(outs))
    	}
    
    	return outs[0].Text, outs[0].Source.String(), nil
    }
    
  • La fonction Cloud Run commence par initialiser les clients Firestore et Pub/Sub. Elle analyse ensuite le message Pub/Sub pour obtenir le texte à traduire et la langue cible souhaitée.

    Puis, l'application attribue un nom unique à la requête de traduction pour s'assurer qu'elle ne stocke pas de traductions en double. Elle procède ensuite à la traduction dans une transaction Firestore afin de s'assurer que les exécutions simultanées n'effectuent pas accidentellement la même traduction deux fois.

    
    // Translate translates the given message and stores the result in Firestore.
    func Translate(ctx context.Context, m PubSubMessage) error {
    	initializeClients()
    
    	t := Translation{}
    	if err := json.Unmarshal(m.Data, &t); err != nil {
    		return fmt.Errorf("json.Unmarshal: %w", err)
    	}
    
    	// Use a unique document name to prevent duplicate translations.
    	key := fmt.Sprintf("%s/%s", t.Language, t.Original)
    	sum := sha512.Sum512([]byte(key))
    	// Base64 encode the sum to make a nice string. The [:] converts the byte
    	// array to a byte slice.
    	docName := base64.StdEncoding.EncodeToString(sum[:])
    	// The document name cannot contain "/".
    	docName = strings.Replace(docName, "/", "-", -1)
    	ref := firestoreClient.Collection("translations").Doc(docName)
    
    	// Run in a transation to prevent concurrent duplicate translations.
    	err := firestoreClient.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
    		doc, err := tx.Get(ref)
    		if err != nil && status.Code(err) != codes.NotFound {
    			return fmt.Errorf("Get: %w", err)
    		}
    		// Do nothing if the document already exists.
    		if doc.Exists() {
    			return nil
    		}
    
    		translated, originalLang, err := translateString(ctx, t.Original, t.Language)
    		if err != nil {
    			return fmt.Errorf("translateString: %w", err)
    		}
    		t.Translated = translated
    		t.OriginalLanguage = originalLang
    
    		if err := tx.Set(ref, t); err != nil {
    			return fmt.Errorf("Set: %w", err)
    		}
    		return nil
    	})
    
    	if err != nil {
    		return fmt.Errorf("RunTransaction: %w", err)
    	}
    	return nil
    }
    

Déployer la fonction Cloud Run

  • Dans Cloud Shell, dans le même répertoire que le fichier translate.go, déployez la fonction Cloud Run avec un déclencheur Pub/Sub:

    gcloud functions deploy Translate --runtime go111 \
    --trigger-topic=translate --set-env-vars GOOGLE_CLOUD_PROJECT=YOUR_GOOGLE_CLOUD_PROJECT

    YOUR_GOOGLE_CLOUD_PROJECT correspond à votre ID de projet Google Cloud.

Comprendre l'application

L'application Web comporte deux composants principaux :

  • Un serveur HTTP Go pour gérer les requêtes Web. Il comporte les deux points de terminaison suivants :
    • / : répertorie toutes les traductions existantes et affiche un formulaire que les utilisateurs peuvent envoyer pour demander de nouvelles traductions.
    • /request-translation : point de terminaison auquel sont envoyés les formulaires. Il publie la requête sur Pub/Sub pour qu'elle soit traduite de manière asynchrone.
  • Un modèle HTML alimenté par les traductions existantes via le serveur Go.

Le serveur HTTP

  • Dans le répertoire index, main.go commence par configurer l'application et enregistrer les gestionnaires HTTP :

    
    // Command index is an HTTP app that displays all previous translations
    // (stored in Firestore) and has a form to request new translations. On form
    // submission, the request is sent to Pub/Sub to be processed in the background.
    package main
    
    import (
    	"context"
    	"encoding/json"
    	"fmt"
    	"html/template"
    	"log"
    	"net/http"
    	"os"
    	"path/filepath"
    
    	"cloud.google.com/go/firestore"
    	"cloud.google.com/go/pubsub"
    	"github.com/GoogleCloudPlatform/golang-samples/getting-started/background"
    )
    
    // topicName is the Pub/Sub topic to publish requests to. The Cloud Function to
    // process translation requests should be subscribed to this topic.
    const topicName = "translate"
    
    // An app holds the clients and parsed templates that can be reused between
    // requests.
    type app struct {
    	pubsubClient    *pubsub.Client
    	pubsubTopic     *pubsub.Topic
    	firestoreClient *firestore.Client
    	tmpl            *template.Template
    }
    
    func main() {
    	projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
    	if projectID == "" {
    		log.Fatalf("GOOGLE_CLOUD_PROJECT must be set")
    	}
    
    	a, err := newApp(projectID, "index")
    	if err != nil {
    		log.Fatalf("newApp: %v", err)
    	}
    
    	http.HandleFunc("/", a.index)
    	http.HandleFunc("/request-translation", a.requestTranslation)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("Listening on localhost:%v", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    // newApp creates a new app.
    func newApp(projectID, templateDir string) (*app, error) {
    	ctx := context.Background()
    
    	pubsubClient, err := pubsub.NewClient(ctx, projectID)
    	if err != nil {
    		return nil, fmt.Errorf("pubsub.NewClient: %w", err)
    	}
    
    	pubsubTopic := pubsubClient.Topic(topicName)
    
    	firestoreClient, err := firestore.NewClient(ctx, projectID)
    	if err != nil {
    		return nil, fmt.Errorf("firestore.NewClient: %w", err)
    	}
    
    	// Template referenced relative to the module/app root.
    	tmpl, err := template.ParseFiles(filepath.Join(templateDir, "index.html"))
    	if err != nil {
    		return nil, fmt.Errorf("template.New: %w", err)
    	}
    
    	return &app{
    		pubsubClient: pubsubClient,
    		pubsubTopic:  pubsubTopic,
    
    		firestoreClient: firestoreClient,
    		tmpl:            tmpl,
    	}, nil
    }
    
  • 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 :

    
    // index lists the current translations.
    func (a *app) index(w http.ResponseWriter, r *http.Request) {
    	docs, err := a.firestoreClient.Collection("translations").Documents(r.Context()).GetAll()
    	if err != nil {
    		log.Printf("GetAll: %v", err)
    		http.Error(w, fmt.Sprintf("Error getting translations: %v", err), http.StatusInternalServerError)
    		return
    	}
    
    	var translations []background.Translation
    	for _, d := range docs {
    		t := background.Translation{}
    		if err := d.DataTo(&t); err != nil {
    			log.Printf("DataTo: %v", err)
    			http.Error(w, "Error reading translations", http.StatusInternalServerError)
    			return
    		}
    		translations = append(translations, t)
    	}
    
    	if err := a.tmpl.Execute(w, translations); err != nil {
    		log.Printf("tmpl.Execute: %v", err)
    		http.Error(w, "Error writing response", http.StatusInternalServerError)
    		return
    	}
    }
    
  • Pour demander de nouvelles traductions, envoyez un formulaire HTML. Le gestionnaire des requêtes de traduction, enregistré sur /request-translation, analyse le formulaire envoyé, valide la requête, puis publie un message dans Pub/Sub :

    
    // requestTranslation parses the request, validates it, and sends it to Pub/Sub.
    func (a *app) requestTranslation(w http.ResponseWriter, r *http.Request) {
    	if err := r.ParseForm(); err != nil {
    		log.Printf("ParseForm: %v", err)
    		http.Error(w, "Bad request", http.StatusBadRequest)
    		return
    	}
    	v := r.PostFormValue("v")
    	if v == "" {
    		log.Printf("Empty value")
    		http.Error(w, "Empty value", http.StatusBadRequest)
    		return
    	}
    	acceptableLanguages := map[string]bool{
    		"de": true,
    		"en": true,
    		"es": true,
    		"fr": true,
    		"ja": true,
    		"sw": true,
    	}
    	lang := r.PostFormValue("lang")
    	if !acceptableLanguages[lang] {
    		log.Printf("Unsupported language: %v", lang)
    		http.Error(w, fmt.Sprintf("Unsupported language: %v", lang), http.StatusBadRequest)
    		return
    	}
    
    	log.Printf("Translation requested: %q -> %s", v, lang)
    
    	t := background.Translation{
    		Original: v,
    		Language: lang,
    	}
    	msg, err := json.Marshal(t)
    	if err != nil {
    		log.Printf("json.Marshal: %v", err)
    		http.Error(w, "Error requesting translation", http.StatusInternalServerError)
    		return
    	}
    
    	res := a.pubsubTopic.Publish(r.Context(), &pubsub.Message{Data: msg})
    	if _, err := res.Get(r.Context()); err != nil {
    		log.Printf("Publish.Get: %v", err)
    		http.Error(w, "Error requesting translation", http.StatusInternalServerError)
    		return
    	}
    }
    

Le modèle HTML

Le modèle HTML représente la base de la page HTML affichée à l'utilisateur afin qu'il puisse consulter les traductions précédentes et en demander de nouvelles. Ce modèle est rempli par le serveur HTTP à l'aide de la liste des traductions existantes.

  • L'élément <head> du modèle HTML inclut des métadonnées, des feuilles de style et le code JavaScript de la page :
    <html>
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Translations</title>
    
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
        <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            $(document).ready(function() {
                $("#translate-form").submit(function(e) {
                    e.preventDefault();
                    // Get value, make sure it's not empty.
                    if ($("#v").val() == "") {
                        return;
                    }
                    $.ajax({
                        type: "POST",
                        url: "/request-translation",
                        data: $(this).serialize(),
                        success: function(data) {
                            // Show snackbar.
                            console.log(data);
                            var notification = document.querySelector('.mdl-js-snackbar');
                            $("#snackbar").removeClass("mdl-color--red-100");
                            $("#snackbar").addClass("mdl-color--green-100");
                            notification.MaterialSnackbar.showSnackbar({
                                message: 'Translation requested'
                            });
                        },
                        error: function(data) {
                            // Show snackbar.
                            console.log("Error requesting translation");
                            var notification = document.querySelector('.mdl-js-snackbar');
                            $("#snackbar").removeClass("mdl-color--green-100");
                            $("#snackbar").addClass("mdl-color--red-100");
                            notification.MaterialSnackbar.showSnackbar({
                                message: 'Translation request failed'
                            });
                        }
                    });
                });
            });
        </script>
        <style>
            .lang {
                width: 50px;
            }
            .translate-form {
                display: inline;
            }
        </style>
    </head>

    Cette page intègre des éléments Material Design Lite (MDL) CSS et JavaScript. MDL vous permet d'apporter un aspect Material Design à vos sites Web.

    JQuery permet d'attendre la fin de chargement du document et de définir un gestionnaire d'envoi de formulaires. Chaque fois que le formulaire de requête de traduction est envoyé, la page effectue une validation minimale du formulaire pour vérifier que la valeur n'est pas vide, puis envoie une requête asynchrone au point de terminaison /request-translation.

    Enfin, un snackbar MDL apparaît pour indiquer si la requête a abouti ou si une erreur s'est produite.

  • Le corps HTML de la page utilise une mise en page MDL et plusieurs composants MDL pour afficher la liste des traductions ainsi qu'un formulaire de requête de traductions supplémentaires :
    <body>
        <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
            <header class="mdl-layout__header">
                <div class="mdl-layout__header-row">
                    <!-- Title -->
                    <span class="mdl-layout-title">Translate with Background Processing</span>
                </div>
            </header>
            <main class="mdl-layout__content">
                <div class="page-content">
                    <div class="mdl-grid">
                    <div class="mdl-cell mdl-cell--1-col"></div>
                        <div class="mdl-cell mdl-cell--3-col">
                            <form id="translate-form" class="translate-form">
                                <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
                                    <input class="mdl-textfield__input" type="text" id="v" name="v">
                                    <label class="mdl-textfield__label" for="v">Text to translate...</label>
                                </div>
                                <select class="mdl-textfield__input lang" name="lang">
                                    <option value="de">de</option>
                                    <option value="en">en</option>
                                    <option value="es">es</option>
                                    <option value="fr">fr</option>
                                    <option value="ja">ja</option>
                                    <option value="sw">sw</option>
                                </select>
                                <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent" type="submit"
                                    name="submit">Submit</button>
                            </form>
                        </div>
                        <div class="mdl-cell mdl-cell--8-col">
                            <table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
                                <thead>
                                    <tr>
                                        <th class="mdl-data-table__cell--non-numeric"><strong>Original</strong></th>
                                        <th class="mdl-data-table__cell--non-numeric"><strong>Translation</strong></th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {{range .}}
                                    <tr>
                                        <td class="mdl-data-table__cell--non-numeric">
                                            <span class="mdl-chip mdl-color--primary">
                                                <span class="mdl-chip__text mdl-color-text--white">{{ .OriginalLanguage }} </span>
                                            </span>
                                        {{ .Original }}
                                        </td>
                                        <td class="mdl-data-table__cell--non-numeric">
                                            <span class="mdl-chip mdl-color--accent">
                                                <span class="mdl-chip__text mdl-color-text--white">{{ .Language }} </span>
                                            </span>
                                            {{ .Translated }}
                                        </td>
                                    </tr>
                                    {{end}}
                                </tbody>
                            </table>
                            <br/>
                            <button class="mdl-button mdl-js-button mdl-button--raised" type="button" onClick="window.location.reload();">
                                Refresh
                            </button>
                        </div>
                    </div>
                </div>
                <div aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-snackbar mdl-js-snackbar" id="snackbar">
                    <div class="mdl-snackbar__text mdl-color-text--black"></div>
                    <button type="button" class="mdl-snackbar__action"></button>
                </div>
            </main>
        </div>
    </body>
    
    </html>

Créer l'application

  • Avant de déployer l'application Web, créez-la pour vous assurer que la compilation s'effectue et que toutes vos dépendances fonctionnent.
    go build -o start ./index
    

    La compilation s'est bien déroulée si aucun message ne s'affiche et si un fichier start est créé.

Déployer l'application Web

Vous pouvez utiliser l'environnement standard App Engine pour créer et déployer une application qui s'exécute de manière fiable, même lorsqu'elle est soumise à une charge importante et doit gérer de grandes quantités de données.

Dans ce tutoriel, l'interface HTTP est déployée dans l'environnement standard App Engine.

Le fichier app.yaml configure l'application App Engine :

runtime: go111
main: index
  • Déployez l'application dans l'environnement standard App Engine à partir du répertoire où se trouve le fichier app.yaml :
    gcloud app deploy

Tester l'application

Après avoir déployé la fonction Cloud Run et l'application App Engine, essayez d'envoyer une requête de traduction.

  1. Pour afficher l'application dans votre navigateur, saisissez l'URL suivante :

    https://PROJECT_ID.REGION_ID.r.appspot.com

    Remplacez les éléments suivants :

    Il s'agit d'une page affichant une liste de traductions vide ainsi qu'un formulaire de requête de nouvelles traductions.

  2. Dans le champ Texte à traduire, saisissez un texte à traduire. Exemple : Hello, World.
  3. Dans la liste déroulante, sélectionnez la langue dans laquelle vous souhaitez traduire le texte.
  4. Cliquez sur Envoyer.
  5. Pour actualiser la page, cliquez sur Actualiser . Une nouvelle ligne s'affiche dans la liste de traduction. 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 application App Engine ou que vous ne voyez pas de nouvelle traduction, vérifiez les éléments suivants :

  1. Vérifiez que les commandes de déploiement gcloud ont bien abouti et n'ont généré aucune erreur. Si des erreurs se sont produites, corrigez-les et essayez Déployer la fonction Cloud Run l'application App Engine.
  2. Dans la console Google Cloud, accédez à la page Visionneuse de journaux.

    Accéder à la page "Visionneuse de journaux"
    1. Dans la liste déroulante Ressources sélectionnées récemment, cliquez sur Application GAE , puis sur Tous les ID de module. La liste des requêtes correspondant à votre accès à l'application s'affiche. Si cette liste n'apparaît pas, vérifiez que vous avez bien sélectionné Tous les ID de module dans la liste déroulante. Si des messages d'erreur s'affichent dans la console Google Cloud, vérifiez que le code de votre application correspond Code présenté dans la section expliquant comment comprendre l'application.
    2. Dans la liste déroulante Ressources sélectionnées récemment, cliquez sur Fonction Cloud, puis sur Tous les noms de fonction. Une fonction est répertoriée pour chaque traduction demandée. Si ce n'est pas le cas, vérifiez que la fonction Cloud Run et l'application App Engine utilisent le même sujet Pub/Sub :
      • Dans le fichier background/index/main.go, vérifiez que la valeur de la constante topicName est "translate".
      • Lorsque vous déployez la fonction Cloud Run, veillez à inclure l'option --trigger-topic=translate.

Effectuer un nettoyage

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Supprimer le projet Google Cloud

  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.

Supprimer l'instance App Engine

  1. In the Google Cloud console, go to the Versions page for App Engine.

    Go to Versions

  2. Select the checkbox for the non-default app version that you want to delete.
  3. Pour supprimer la version de l'application, cliquez sur  Supprimer.

Supprimer la fonction Cloud Run

  • Supprimez la fonction Cloud Run que vous avez créée dans ce tutoriel:
    gcloud functions delete Translate

Étape suivante