Criar tarefas push

Esta página descreve como criar tarefas e colocá-las em filas de envio. Quando quiser processar uma tarefa, tem de criar um novo objeto de tarefa e colocá-lo numa fila. Pode especificar explicitamente o serviço e o controlador que processam a tarefa e, opcionalmente, transmitir dados específicos da tarefa ao controlador. Também pode ajustar a configuração da tarefa, como agendar uma hora no futuro em que deve ser executada ou limitar o número de vezes que quer que a tarefa seja repetida se falhar.

Criar uma nova tarefa

Para criar e colocar uma tarefa na fila, obtenha um Queue através de QueueFactory e chame o respetivo método add(). Pode obter uma fila com nome especificado no ficheiro queue.xml usando o método getQueue() da fábrica ou pode obter a fila predefinida usando getDefaultQueue(). Pode chamar o método Queue's add() com uma instância TaskOptions (produzida por TaskOptions.Builder) ou pode chamá-lo sem argumentos para criar uma tarefa com as opções predefinidas para a 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));

Especificar o serviço de trabalho

Quando uma tarefa é removida da respetiva fila, o serviço Task Queue envia-a para um serviço de trabalho. Cada tarefa tem um alvo e um URL, que determinam que serviço e controlador vão realizar a tarefa em última instância.

target

O destino especifica o serviço que vai receber o pedido HTTP para realizar a tarefa. É uma string que especifica um serviço/versão/instância numa das formas canónicas. Os formulários mais usados são:

    service
    version.service
    instance.version.service

A string de destino é adicionada antes do nome do domínio da sua app. Existem três formas de definir o destino de uma tarefa:

  • Declare o destino quando construir a tarefa. Pode definir o destino explicitamente quando cria a tarefa definindo o cabeçalho Host usando TaskOptions:

    taskOptions.header("Host", versionHostname)
    

  • Inclua uma diretiva target quando definir uma fila no elemento queue.xml, como na definição de queue-blue. Todas as tarefas adicionadas a uma fila com um target usam esse destino, mesmo que tenha sido atribuído um destino diferente à tarefa no momento da criação.

  • Se não for especificado nenhum destino de acordo com qualquer um dos dois métodos anteriores, o destino da tarefa é a versão do serviço que a coloca na fila. Tenha em atenção que, se colocar uma tarefa em fila a partir do serviço e da versão predefinidos desta forma, e a versão predefinida mudar antes da execução da tarefa, esta é executada na nova versão predefinida.

url

O url seleciona um dos controladores no serviço de destino, que vai realizar a tarefa.

O url deve corresponder a um dos padrões de URL do controlador no serviço de destino. O url pode incluir parâmetros de consulta se o método especificado na tarefa for GET ou PULL. Se não for especificado nenhum url, é usado o URL predefinido /_ah/queue/[QUEUE_NAME], onde [QUEUE_NAME] é o nome da fila da tarefa.

Transmitir dados ao controlador

Pode transmitir dados ao controlador como parâmetros de consulta no URL da tarefa, mas apenas se o método especificado na tarefa for GET ou PULL.

O construtor TaskOptions.Builder tem métodos para adicionar dados como a carga útil do pedido HTTP e como parâmetros, que são adicionados ao URL como parâmetros de consulta.

params
Não especifique parâmetros se estiver a usar o método POST juntamente com uma carga útil ou se estiver a usar o método GET e tiver incluído um URL com parâmetros de consulta.

Atribuir um nome a uma tarefa

Quando cria uma nova tarefa, o App Engine atribui-lhe um nome único por predefinição. No entanto, pode atribuir o seu próprio nome a uma tarefa através do parâmetro name. Uma vantagem de atribuir os seus próprios nomes de tarefas é que as tarefas com nome são desduplicadas, o que significa que pode usar nomes de tarefas para garantir que uma tarefa só é adicionada uma vez. A remoção de duplicados continua durante 9 dias após a conclusão ou a eliminação da tarefa.

Tenha em atenção que a lógica de remoção de duplicados introduz uma sobrecarga de desempenho significativa, o que resulta num aumento das latências e, potencialmente, das taxas de erro associadas a tarefas com nome. Estes custos podem aumentar significativamente se os nomes das tarefas forem sequenciais, como com as datas/horas. Assim, se atribuir os seus próprios nomes, recomendamos que use um prefixo bem distribuído para os nomes das tarefas, como um hash dos conteúdos.

Se atribuir os seus próprios nomes às tarefas, tenha em atenção que o comprimento máximo do nome é de 500 carateres e que o nome pode conter letras maiúsculas e minúsculas, números, sublinhados e hífenes.

Adicionar tarefas de forma assíncrona

Por predefinição, as chamadas que adicionam tarefas a filas são síncronas. Na maioria dos cenários, as chamadas síncronas funcionam bem. Adicionar uma tarefa a uma fila é normalmente uma operação rápida. Existe uma pequena percentagem de operações de adição de tarefas que podem demorar significativamente mais tempo, mas o tempo médio para adicionar uma tarefa é inferior a 5 ms.

Não é possível processar em lote operações de tarefas adicionadas a filas diferentes. Por isso, a API Task Queue também oferece chamadas assíncronas que lhe permitem adicionar estas tarefas em paralelo, minimizando ainda mais esta latência. Isto é útil se estiver a criar uma aplicação extremamente sensível à latência que precise de realizar várias operações de adição de tarefas a diferentes filas ao mesmo tempo.

Se quiser fazer chamadas assíncronas para uma fila de tarefas, use os métodos assíncronos fornecidos pela classe Queue. Chame get no Future devolvido para forçar a conclusão do pedido. Quando adiciona tarefas de forma assíncrona numa transação, deve chamar get() no Future antes de confirmar a transação para garantir que o pedido foi concluído.

Colocar tarefas em fila em transações do Cloud Datastore

Pode colocar uma tarefa em fila como parte de uma transação do Datastore, de modo que a tarefa só seja colocada em fila (e seja garantido que é colocada em fila) se a transação for confirmada com êxito. As tarefas adicionadas numa transação são consideradas parte da mesma e têm o mesmo nível de isolamento e consistência.

Uma aplicação não pode inserir mais de cinco tarefas transacionais em filas de tarefas durante uma única transação. As tarefas transacionais não podem ter nomes especificados pelo utilizador.

O seguinte exemplo de código demonstra como inserir tarefas transacionais numa fila de envio 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) {
}

Usar as DeferredTasks em vez de um serviço de trabalho

A configuração de um controlador para cada tarefa distinta (conforme descrito nas secções anteriores) pode ser complexa, tal como a serialização e a desserialização de argumentos complexos para a tarefa, especialmente se tiver muitas tarefas diversas, mas pequenas, que quer executar na fila. O SDK Java inclui uma interface denominada DeferredTask. Esta interface permite-lhe definir uma tarefa como um único método. Esta interface usa a serialização Java para agrupar uma unidade de trabalho numa fila de tarefas. Um simples retorno desse método é considerado um sucesso. A geração de qualquer exceção a partir 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!");
}

Trabalhar com tarefas numa aplicação multi-inquilino

Por predefinição, as filas de envio usam o espaço de nomes atual, conforme definido no gestor de espaços de nomes no momento em que a tarefa é criada. Se a sua aplicação usar a funcionalidade multiinquilino, consulte a API Java 8 de espaços de nomes.

O que se segue?