Primeiros passos: filas de tarefas

Saiba como usar filas de tarefas e a App Engine Image API para redimensionar as imagens.

As filas de tarefas executam o código fora da interação direta do usuário, permitindo que as tarefas sejam realizadas em segundo plano. Este guia usa uma fila de tarefas para executar tarefas após adicionar uma imagem ao Cloud Storage. As tarefas a serem realizadas na fila de tarefas são estas:

  1. Recupere o arquivo de imagem que acabou de ser enviado para o Cloud Storage.
  2. Redimensione-o para uma imagem em miniatura usando a Image API.
  3. Armazene a miniatura resultante no Cloud Storage.

O ambiente de execução Java 8 do App Engine também é compatível com classes de manipulação de imagens nativas do Java, como AWT e Java2D (ambos em inglês).

Antes de começar

  1. Configure o ambiente de desenvolvimento e crie o projeto do App Engine.

  2. Este guia usa a biblioteca de IOUtils Apache Commons. Para incluir a biblioteca de IOUtils no projeto do App Engine:

    Adicione ao seu pom.xml:

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

Como importar bibliotecas

O código de amostra fornecido com este guia usa as seguintes importações:

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;

Como criar uma fila de tarefas

O App Engine fornece uma fila de tarefas default, mas é possível criar filas de tarefas diferentes para tipos de trabalho distintos. Por exemplo, é possível criar uma fila de tarefas para redimensionar imagens e outra para atualizar o banco de dados do aplicativo.

Para adicionar filas, crie o arquivo queue.xml no diretório WEB-INF do projeto do App Engine. Uma fila de tarefas precisa especificar um nome e uma taxa de execução:

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

Essa fila de exemplo, chamada resize-image, define uma taxa de execução de 60 vezes por hora, ou uma vez por minuto. Para ver as opções da fila de listagem completa, consulte a referência queue.xml.

Uma fila de tarefas tem dois componentes: o solicitante e um gerenciador. O solicitante adiciona uma tarefa à fila e a envia para o gerenciador de tarefas.

Como adicionar tarefas a uma fila

Para adicionar uma tarefa a uma fila:

  1. Crie um objeto de fila de tarefas usando QueueFactory.getQueue(). Especifique o nome da fila definido em 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. Adicione tarefas ao objeto Queue. Conforme o exemplo de código, imageResizeQueue.add() adiciona uma tarefa ao objeto imageResizeQueue:

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

    Especifique o URI do gerenciador de tarefas usando TaskOptions.Builder.withUrl() com os parâmetros enviados ao gerenciador.

    Neste exemplo, o URI é /tasks/imageresize e o parâmetro é uma variável chamada filename que contém o nome do arquivo da imagem a ser processada.

Como criar um gerenciador de tarefas

Depois que você tiver adicionado uma tarefa à fila, o gerenciador de tarefas mapeado para o URI /tasks/imageresize será executado. Gerenciador de tarefas é um Servlet Java que tenta executar a tarefa até ser bem-sucedido.

Neste exemplo, o gerenciador faz três tarefas:

  • Recuperar a imagem especificada pelo autor da chamada do Cloud Storage.

  • Transformar a imagem usando a App Engine Image API. Neste exemplo, uma imagem em miniatura.

  • Armazenar a imagem transformada (miniatura) no Cloud Storage.

Para criar o gerenciador de tarefas:

  1. Adicione uma anotação que mapeie o gerenciador para o URI /tasks/imageresize:

     @WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize")
     public class imageResize extends HttpServlet {
    
       // Task handler functionality
    
     }
    
  2. Configure uma conexão com o Cloud Storage conforme documentado no guia Como usar o Cloud Storage e recupere a imagem do Cloud Storage:

     public void init() throws ServletException {
    
      // initiate GcsService
      GcsService gcsService =
        GcsServiceFactory.createGcsService(
            new RetryParams.Builder()
                .initialRetryDelayMillis(10)
                .retryMaxAttempts(10)
                .totalRetryPeriodMillis(15000)
                .build());
    }
    
  3. Processe a solicitação da fila de tarefas de entrada usando o nome do arquivo fornecido para recuperar a imagem do 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. Use o objeto ImagesService para fazer o redimensionamento da imagem:

    // 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()));
    

    O snippet acima usa o método makeResize() da API Image para redimensionar a imagem para uma miniatura. Para fazer isso, ele lê a imagem do Cloud Storage em um InputChannel e a converte em um ByteArray usando IOUtils.toByteArray().

    Depois de aplicar a transformação, a nova imagem tem a string thumbnail_ anexada ao nome de arquivo, permissão definida para ser publicamente legível e gravada no Cloud Storage.

Como proteger URLs do gerenciador de tarefas

Proteja tarefas que realizem operações confidenciais, como modificar dados. Dessa forma, usuários externos não podem chamá-los diretamente. Você pode fazer isso restringindo o acesso à tarefa a administradores do App Engine, o que evita que usuários acessem URLs de tarefa. Essa restrição não se aplica a solicitações de tarefas provenientes do aplicativo do App Engine.

No exemplo atual, os gerenciadores de tarefas têm URLs na pasta /tasks/. Para restringir o acesso à pasta /tasks/ aos administradores do App Engine, adicione o seguinte ao web.xml do projeto.

<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>

Como remover uma única tarefa de uma fila

Para remover uma única tarefa de uma fila, use deleteTask():

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

Como remover todas as tarefas de uma fila

Para remover todas as tarefas de uma fila, use purge(). A limpeza pode demorar até um minuto para remover todas as tarefas na fila.

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

Pode demorar um minuto para remover todas as tarefas de uma fila. Dessa forma, você precisa esperar alguns segundos até adicionar novas tarefas à fila.

Como excluir uma fila de tarefas

Para excluir uma fila de tarefas, remova a entrada do arquivo queue.xml do projeto e reimplante.

Como implantar no App Engine

Implante o app no App Engine usando o Maven.

Acesse o diretório raiz do projeto e digite:

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

Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Se o arquivo pom.xmlespecificar o ID do projeto, não será necessário incluir a propriedade -Dapp.deploy.projectId no comando executado.

Depois que o Maven implantar o aplicativo, digite o comando a seguir para abrir uma guia do navegador da Web automaticamente em seu novo aplicativo:

gcloud app browse

Próximas etapas

Este guia mostra como usar uma fila de tarefas para criar uma miniatura de imagem e armazená-la no Cloud Storage. Ele pode ser usado com outros serviços de armazenamento, como Cloud Datastore ou Cloud SQL.