Criar tarefas push

Nesta página, descrevemos como criar tarefas e colocá-las em filas push. Quando quiser processar uma tarefa, você precisará criar um novo objeto de tarefa e colocá-lo em uma fila. É possível especificar explicitamente o serviço e o gerenciador que processam a tarefa e, como opção, passar dados específicos da tarefa para o gerenciador. Também é possível ajustar a configuração da tarefa, como agendá-la caso ela precise ser executada posteriormente ou limitar o número de vezes que a tarefa será repetida se falhar.

Como criar uma nova tarefa

Para criar uma tarefa e colocá-la em fila, receba um Queue usando QueueFactory e chame o respectivo método add(). É possível receber uma fila nomeada especificada no arquivo queue.xml usando o método getQueue() da fábrica ou receber a fila padrão usando getDefaultQueue(). É possível chamar o método add() de Queue com uma instância TaskOptions (produzida por TaskOptions.Builder) ou chamá-lo sem argumentos para criar uma tarefa com as opções padrão da fila.

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
Queue queue = QueueFactory.getDefaultQueue();
queue.add(TaskOptions.Builder.withUrl("/worker").param("key", key));

Como especificar o service worker

Quando uma tarefa é retirada da fila, o serviço de fila de tarefas a envia para um serviço de worker. Cada tarefa tem um destino e um url, que determinam o serviço e o gerenciador que realizarão a tarefa.

target

O destino especifica o serviço que receberá a solicitação HTTP para executar a tarefa. É uma string que especifica um serviço/versão/instância em qualquer uma das formas canônicas. As formas mais usadas são estas:

    service
    version.service
    instance.version.service

A string de destino precede o nome de domínio do aplicativo. Há três maneiras de definir o destino de uma tarefa:

  • Declarar o destino ao construir a tarefa. Para definir explicitamente o destino ao criar a tarefa, defina o cabeçalho Host usando TaskOptions:

    taskOptions.header("Host", versionHostname)
    

  • Incluir uma diretiva target ao definir uma fila em queue.xml, como na definição de queue-blue. Todas as tarefas incluídas em uma fila com um target terão esse destino, mesmo que outro destino seja atribuído à tarefa no momento da criação.

  • Se nenhum destino for especificado de acordo com um dos dois métodos anteriores, o destino da tarefa será a versão do serviço que a põe em fila. Se você colocar em fila uma tarefa do serviço e versão padrões dessa maneira, e a versão padrão mudar antes da execução da tarefa, esta será executada na nova versão padrão.

url

Com url, um dos gerenciadores no serviço de destino é selecionado para a execução da tarefa.

O url precisa corresponder a um dos padrões de URL do gerenciador no serviço de destino. O url poderá incluir parâmetros de consulta se o método especificado na tarefa for GET ou PULL. Se nenhum url for especificado, será usado o URL /_ah/queue/[QUEUE_NAME] padrão, em que [QUEUE_NAME] corresponde ao nome da fila da tarefa.

Como passar dados ao gerenciador

É possível passar dados como parâmetros de consulta ao gerenciador no URL da tarefa, mas somente se o método especificado na tarefa for GET ou PULL.

O construtor TaskOptions.Builder tem métodos para adicionar dados como payload da solicitação HTTP e como parâmetros, que são adicionados ao URL como parâmetros de consulta.

params
Não especifique parâmetros ao usar o método POST junto a um payload ou se usar o método GET e tiver incluído um URL com parâmetros de consulta.

Como nomear uma tarefa

Quando você cria uma nova tarefa, o App Engine atribui a ela um nome exclusivo por padrão. Porém, é possível atribuir à tarefa um nome de sua escolha usando o parâmetro name. Uma vantagem de escolher os nomes que serão atribuídos às tarefas é a remoção de duplicações, o que significa que é possível usar os nomes de tarefas para garantir que a tarefa seja adicionada apenas uma vez. A remoção de duplicações continua por nove dias após a conclusão ou exclusão da tarefa.

Observe que essa lógica de remoção de duplicações apresenta uma sobrecarga de desempenho significativa, resultando em maiores latências e taxas de erro potencialmente maiores associadas a tarefas nomeadas. Esses custos poderão aumentar significativamente se os nomes das tarefas forem sequenciais, como no caso de carimbos de data/hora. Então, se você mesmo atribuir os nomes, recomendamos usar um prefixo bem distribuído para nomes de tarefas, como um hash do conteúdo.

Se você mesmo atribuir nomes às tarefas, observe que o comprimento máximo do nome é 500 caracteres. O nome pode conter letras maiúsculas e minúsculas, sublinhados de números e hifens.

Como adicionar tarefas de maneira assíncrona

Por padrão, as chamadas que adicionam tarefas a filas são síncronas. As chamadas síncronas funcionam bem na maioria dos cenários. Adicionar uma tarefa a uma fila costuma ser uma operação rápida. O tempo médio para adicionar uma tarefa é de menos de 5 ms. Porém, uma pequena porcentagem dessas operações pode demorar muito mais tempo.

Não é possível colocar em lote as operações de adição de tarefas a filas diferentes. Por isso, a API Task Queue também fornece chamadas assíncronas que permitem adicionar essas tarefas em paralelo, minimizando ainda mais essa latência. Isso é útil para a criação de um aplicativo muito sensível à latência que precisa executar diversas operações de adição de tarefas a diferentes filas ao mesmo tempo.

Se você quiser fazer chamadas assíncronas a uma fila de tarefas, use os métodos assíncronos fornecidos pela classe Queue. Chame get no Future retornado para forçar a conclusão da solicitação. Ao adicionar tarefas a uma transação de maneira assíncrona, chame get() na classe Future antes de confirmar a transação para garantir que a solicitação foi concluída.

Como enfileirar tarefas em transações do Cloud Datastore

É possível enfileirar uma tarefa como parte de uma transação do Datastore, de modo que ela seja enfileirada e garantida para ser enfileirada apenas se a transação for confirmada. As tarefas adicionadas a uma transação são consideradas parte dela e têm o mesmo nível de isolamento e consistência.

Um aplicativo não pode inserir mais de cinco tarefas transacionais a filas de tarefas durante uma única transação. Tarefas transacionais não podem ter nomes especificados pelo usuário.

O exemplo de código a seguir demonstra como inserir tarefas transacionais em uma fila push como parte de uma transação do Datastore:

DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Queue queue = QueueFactory.getDefaultQueue();
try {
    Transaction txn = ds.beginTransaction();

    // ...

    queue.add(TaskOptions.Builder.withUrl("/path/to/my/worker"));

    // ...
    txn.commit();
} catch (DatastoreFailureException e) {
}

Como usar DeferredTasks em vez de um serviço de worker

A configuração de um gerenciador para cada tarefa distinta, conforme descrito nas seções anteriores, pode ser complicada. A serialização e desserialização de argumentos complexos para a tarefa também podem ter complicações, especialmente se você quiser executar na fila muitas tarefas distintas, porém, pequenas. O Java SDK inclui uma interface denominada DeferredTask, que permite definir uma tarefa como um método único. A interface usa a serialização do Java para empacotar uma unidade de trabalho em uma fila de tarefas. Um retorno simples desse método é considerado sucesso. A geração de qualquer exceção desse método é considerada uma falha.


/** A hypothetical expensive operation we want to defer on a background task. */
public static class ExpensiveOperation implements DeferredTask {

  @Override
  public void run() {
    System.out.println("Doing an expensive operation...");
    // expensive operation to be backgrounded goes here
  }
}

/**
 * Basic demonstration of adding a deferred task.
 *
 * @param request servlet request
 * @param resp servlet response
 */
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse resp)
    throws IOException {
  // Add the task to the default queue.
  Queue queue = QueueFactory.getDefaultQueue();

  // Wait 5 seconds to run for demonstration purposes
  queue.add(
      TaskOptions.Builder.withPayload(new ExpensiveOperation())
          .etaMillis(System.currentTimeMillis() + DELAY_MS));

  resp.setContentType("text/plain");
  resp.getWriter().println("Task is backgrounded on queue!");
}

Como trabalhar com tarefas em um aplicativo multilocatário

Por padrão, as filas push usam o namespace atual configurado no gerenciador de namespace no momento da criação da tarefa. Se o aplicativo usa multilocação, consulte a API Namespaces Java 8.

A seguir