Premier pas avec les files d'attente de tâches

Apprenez à utiliser les files d'attente de tâches et l'API Image d'App Engine pour redimensionner les images.

Les files d'attente de tâches exécutent le code sans l'interaction directe de l'utilisateur, permettant ainsi aux tâches d'être effectuées en arrière-plan. Ce guide utilise une file d'attente de tâches pour effectuer des tâches après avoir ajouté une image à Cloud Storage. Les tâches à exécuter, qui se trouvent dans la file d'attente, sont les suivantes :

  1. Récupérer le fichier image qui vient d'être importé dans Cloud Storage
  2. Redimensionner cette image sous forme de vignette à l'aide de l'API Image
  3. Stocker la vignette obtenue dans Cloud Storage

L'environnement d'exécution Java 8 d'App Engine accepte également les classes de manipulation d'images natives de Java, telles que AWT et Java2D.

Avant de commencer

  1. Configurez votre environnement de développement et créez votre projet App Engine.

  2. Ce guide utilise la bibliothèque Apache Commons IOUtils. Pour l'inclure dans votre projet App Engine, procédez comme suit :

    Ajoutez ceci au fichier pom.xml :

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>
    

Importer des bibliothèques

L'exemple de code fourni dans ce guide utilise les importations suivantes :

import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Transform;
import org.apache.commons.io.IOUtils;

Créer une file d'attente de tâches

Bien qu'App Engine fournisse une file d'attente de tâches default, vous pouvez créer plusieurs files d'attente pour différents types de tâches. Par exemple, vous pouvez créer une file d'attente de tâches pour redimensionner les images, et une autre pour mettre à jour la base de données de votre application.

Pour ajouter des files d'attente, créez le fichier queue.xml dans le répertoire WEB-INF de votre projet App Engine. Une file d'attente doit spécifier un nom et un taux d'exécution :

<?xml version="1.0" encoding="UTF-8"?>
  <queue-entries>
    <queue>
      <name>resize-image</name>
      <rate>60/h</rate>
    </queue>
  </queue-entries>

Dans cet exemple, la file d'attente nommée resize-image définit un taux d'exécution de 60 tâches par heure, soit une tâche par minute. Pour découvrir la liste complète des options de files d'attente, consultez la documentation de référence sur queue.xml.

Une file d'attente de tâches comprend deux composants : le demandeur de tâches et un gestionnaire de tâches. Le demandeur ajoute une tâche à la file d'attente et l'envoie au gestionnaire de tâches.

Ajouter des tâches à une file d'attente

Pour ajouter une tâche à une file d'attente, procédez comme suit :

  1. Créez un objet de file d'attente de tâches à l'aide de QueueFactory.getQueue(), en veillant à spécifier le nom de la file d'attente défini dans le fichier queue.xml :

    Queue imageResizeQueue; // Taskqueue queue
    
    @Override
    public void init() throws ServletException {
    
      // Setup Cloud Storage service
      gcsService =
          GcsServiceFactory.createGcsService(
              new RetryParams.Builder()
                  .initialRetryDelayMillis(10)
                  .retryMaxAttempts(10)
                  .totalRetryPeriodMillis(15000)
                  .build());
    
      // Initialize the queue object with the specific queue
      imageResizeQueue = QueueFactory.getQueue([QUEUE-NAME]);
    
      // Cloud SQL connection setup
      try {
        final String url = System.getProperty("cloudsql"); // Cloud SQL server URI
    
        try {
          conn = DriverManager.getConnection(url); // Connect to the database
    
          Statement createTable; // SQL statement
    
          // Batch SQL table creation commands
          createTable.addBatch(createContentTableSql);
          createTable.addBatch(createUserTableSql);
          createTable.addBatch(createImageTableSql);
          createTable.addBatch(createBlogPostImageTableSql);
          conn.createTable.executeBatch(); // Execute batch
    
        } catch (SQLException e) {
          throw new ServletException("Unable to connect to Cloud SQL", e);
        }
    
      } finally {
        // Nothing really to do here.
      }
    
    }
    
  2. Ajoutez des tâches à l'objet Queue. Comme illustré dans l'exemple de code, imageResizeQueue.add() ajoute une tâche à l'objet imageResizeQueue :

    try {
      // Add a queued task to create a thumbnail of the uploaded image
      imageResizeQueue.add(
          TaskOptions.Builder.withUrl("/tasks/imageresize").param("filename", filename));
    }
    

    Spécifiez l'URI du gestionnaire de tâches à l'aide de TaskOptions.Builder.withUrl(), ainsi que tous les paramètres envoyés au gestionnaire.

    Dans cet exemple, l'URI est /tasks/imageresize et le paramètre est une variable appelée filename qui contient le nom de fichier de l'image à traiter.

Créer un gestionnaire de tâches

Une fois que vous avez ajouté une tâche à la file d'attente, le gestionnaire de tâches mappé avec l'URI /tasks/imageresize s'exécute. Il s'agit d'un servlet Java qui tente d'exécuter la tâche jusqu'à ce qu'elle aboutisse.

Dans cet exemple, le gestionnaire de tâches exécute trois tâches :

  • Récupérer l'image spécifiée par le demandeur dans Cloud Storage

  • Convertir l'image à l'aide de l'API Image d'App Engine, dans cet exemple, en vignette

  • Stocker l'image convertie (vignette) dans Cloud Storage

Pour créer le gestionnaire de tâches, procédez comme suit :

  1. Ajoutez une annotation qui mappe le gestionnaire avec l'URI /tasks/imageresize :

     @WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize")
     public class imageResize extends HttpServlet {
    
       // Task handler functionality
    
     }
    
  2. Configurez une connexion à Cloud Storage comme indiqué dans le guide d'utilisation de Cloud Storage, puis récupérez l'image dans Cloud Storage :

     public void init() throws ServletException {
    
      // initiate GcsService
      GcsService gcsService =
        GcsServiceFactory.createGcsService(
            new RetryParams.Builder()
                .initialRetryDelayMillis(10)
                .retryMaxAttempts(10)
                .totalRetryPeriodMillis(15000)
                .build());
    }
    
  3. Traitez la requête entrante de la file de tâches en utilisant le nom de fichier fourni pour récupérer l'image dans Cloud Storage :

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    
      String filename = req.getParameter("filename"); // Get the filename passed from the task requestor
      GcsFilename gcsFile = new GcsFilename(bucket, filename); // Create a valid Cloud Storage filename
    
      GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(gcsFile, 0, BUFFER_SIZE); // Read the file from Cloud Storage
    
  4. Utilisez l'objet ImagesService afin de redimensionner l'image :

    // Get an instance of the ImagesService we can use to transform images.
    ImagesService imagesService = ImagesServiceFactory.getImagesService();
    
    // Make an image directly from a byte array, and transform it.
    Image image =
        ImagesServiceFactory.makeImage(IOUtils.toByteArray(Channels.newInputStream(readChannel)));
    Transform resize = ImagesServiceFactory.makeResize(100, 50); // resize image to 100x50
    Image resizedImage = imagesService.applyTransform(resize, image);
    
    // Write the transformed image back to a Cloud Storage object.
    gcsService.createOrReplace(
        new GcsFilename(bucket, "thumbnail_" + filename),
        new GcsFileOptions.Builder().acl("public-read").build(),
        ByteBuffer.wrap(resizedImage.getImageData()));
    

    L'extrait ci-dessus se sert de la méthode makeResize() de l'API Image pour redimensionner l'image en vignette. Pour ce faire, il lit l'image de Cloud Storage dans un InputChannel et la convertit en ByteArray à l'aide de IOUtils.toByteArray().

    Après avoir appliqué la transformation, la nouvelle image comporte la chaîne thumbnail_ ajoutée à son nom de fichier, l'autorisation étant définie pour être lisible et écrite publiquement dans Cloud Storage.

Sécuriser les URL du gestionnaire de tâches

Vous devez sécuriser les tâches qui effectuent des opérations sensibles, telles que la modification des données, afin que les utilisateurs externes ne puissent pas les appeler directement. Pour ce faire, vous pouvez limiter l'accès aux tâches aux administrateurs App Engine, ce qui empêche les utilisateurs d'accéder aux URL de tâches. Notez que cette restriction ne s'applique pas aux requêtes de tâches provenant de votre application App Engine.

Dans cet exemple, les gestionnaires de tâches ont des URL dans le dossier /tasks/. Pour restreindre l'accès au dossier /tasks/ aux administrateurs App Engine, ajoutez ce qui suit au fichier web.xml du projet.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>tasks</web-resource-name>
        <url-pattern>/tasks/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

Supprimer une seule tâche d'une file d'attente

Pour supprimer une seule tâche d'une file d'attente, servez-vous de la méthode deleteTask() :

private void removeTask(Queue queue, String taskName) {
  queue.deleteTask(taskName); // remove a task from a queue
}

Supprimer toutes les tâches d'une file d'attente

Pour supprimer toutes les tâches d'une file d'attente, utilisez purge(). L'opération peut prendre jusqu'à une minute.

private void purgeQueue(Queue queue) {
  queue.purge(); // remove all tasks from a queue
}

Comme la suppression de toutes les tâches d'une file d'attente peut durer une minute, vous devez attendre quelques secondes avant d'ajouter d'autres tâches à la file d'attente.

Supprimer une file d'attente de tâches

Pour supprimer une file d'attente de tâches, supprimez l'entrée du fichier queue.xml du projet et procédez au redéploiement.

Déployer sur App Engine

Vous pouvez déployer votre application sur App Engine à l'aide de Maven.

Accédez au répertoire racine de votre projet, puis saisissez la commande suivante :

mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID

Remplacez PROJECT_ID par l'ID de votre projet Google Cloud. Si votre ID de projet est déjà inclus dans le fichier pom.xml, vous n'avez pas besoin d'inclure la propriété -Dapp.deploy.projectId dans la commande que vous exécutez.

Lorsque Maven a déployé votre application, saisissez la commande ci-dessous pour ouvrir un onglet de navigateur Web automatiquement dans votre nouvelle application :

gcloud app browse

Étape suivante

Ce guide explique comment créer une miniature d'une image à l'aide d'une file d'attente de tâches et la stocker dans Cloud Storage. Ce service peut s'utiliser en complément d'autres services de stockage tels que Cloud Datastore ou Cloud SQL.