Crear tareas de salida

En esta página se describe cómo crear tareas y colocarlas en colas de salida. Cuando quieras procesar una tarea, debes crear un objeto de tarea y colocarlo en una cola. Puedes especificar de forma explícita el servicio y el controlador que procesan la tarea, así como enviar datos específicos de la tarea al controlador (opcional). También puedes ajustar la configuración de la tarea, como programar una hora en el futuro para que se ejecute o limitar el número de veces que quieres que se vuelva a intentar si falla.

Crear una tarea

Para crear y poner en cola una tarea, obtén un objeto Queue mediante QueueFactory y llama a su método add(). Puedes obtener una cola con nombre especificada en el archivo queue.xml mediante el método getQueue() de la fábrica o la cola predeterminada mediante getDefaultQueue(). Puedes llamar al método add() de Queue con una instancia de TaskOptions (producida por TaskOptions.Builder) o puedes llamarlo sin argumentos para crear una tarea con las opciones predeterminadas de la cola.

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 el servicio de trabajador

Cuando se extrae una tarea de su cola, el servicio de colas de tareas la envía a un servicio de trabajador. Cada tarea tiene un destino y una URL, que determinan qué servicio y controlador realizarán la tarea en última instancia.

target

El destino especifica el servicio que recibirá la solicitud HTTP para realizar la tarea. Es una cadena que especifica un servicio, una versión o una instancia en cualquiera de los formatos canónicos. Los formularios que se usan con más frecuencia son los siguientes:

    service
    version.service
    instance.version.service

La cadena de destino se añade al principio del nombre de dominio de tu aplicación. Hay tres formas de definir el destino de una tarea:

  • Declara el destino al crear la tarea. Puedes definir el destino explícitamente al crear la tarea. Para ello, define el encabezado Host con TaskOptions:

    taskOptions.header("Host", versionHostname)
    

  • Incluye una directiva target cuando definas una cola en queue.xml, como en la definición de queue-blue. Todas las tareas añadidas a una cola con un target usarán ese objetivo, aunque se haya asignado otro objetivo a la tarea en el momento de la creación.

  • Si no se especifica ningún destino según ninguno de los dos métodos anteriores, el destino de la tarea es la versión del servicio que la pone en cola. Ten en cuenta que, si pones en cola una tarea del servicio y la versión predeterminados de esta forma, y la versión predeterminada cambia antes de que se ejecute la tarea, se ejecutará en la nueva versión predeterminada.

url

url selecciona uno de los controladores del servicio de destino, que realizará la tarea.

El url debe coincidir con uno de los patrones de URL del controlador del servicio de destino. El url puede incluir parámetros de consulta si el método especificado en la tarea es GET o PULL. Si no se especifica ningún url, se usa la URL predeterminada /_ah/queue/[QUEUE_NAME], donde [QUEUE_NAME] es el nombre de la cola de la tarea.

Transferir datos al controlador

Puede transferir datos al controlador como parámetros de consulta en la URL de la tarea, pero solo si el método especificado en la tarea es GET o PULL.

El constructor TaskOptions.Builder tiene métodos para añadir datos como carga útil de la solicitud HTTP y como parámetros, que se añaden a la URL como parámetros de consulta.

params
No especifiques parámetros si utilizas el método POST junto con una carga útil o si utilizas el método GET y has incluido una URL con parámetros de consulta.

Poner nombre a una tarea

Cuando creas una tarea, App Engine le asigna un nombre único de forma predeterminada. Sin embargo, puedes asignar tu propio nombre a una tarea usando el parámetro name. Una de las ventajas de asignar tus propios nombres a las tareas es que las tareas con nombre no se duplican, lo que significa que puedes usar nombres de tareas para garantizar que una tarea solo se añada una vez. La desduplicación continúa durante 9 días después de que se complete o se elimine la tarea.

Tenga en cuenta que la lógica de desduplicación introduce una sobrecarga de rendimiento significativa, lo que provoca un aumento de la latencia y, posiblemente, de las tasas de error asociadas a las tareas con nombre. Estos costes pueden aumentar significativamente si los nombres de las tareas son secuenciales, como ocurre con las marcas de tiempo. Por lo tanto, si asignas tus propios nombres, te recomendamos que utilices un prefijo bien distribuido para los nombres de las tareas, como un hash del contenido.

Si asignas tus propios nombres a las tareas, ten en cuenta que la longitud máxima del nombre es de 500 caracteres y que puede contener letras mayúsculas y minúsculas, números, guiones bajos y guiones.

Agregar tareas asincrónicamente

De forma predeterminada, las llamadas que añaden tareas a las colas son síncronas. En la mayoría de los casos, las llamadas síncronas funcionan correctamente. Añadir una tarea a una cola suele ser una operación rápida. Un pequeño porcentaje de las operaciones de añadir tareas pueden tardar mucho más, pero el tiempo medio para añadir una tarea es inferior a 5 ms.

Las operaciones de adición de tareas a diferentes colas no se pueden agrupar, por lo que la API Task Queue también proporciona llamadas asíncronas que te permiten añadir estas tareas en paralelo, lo que reduce aún más esta latencia. Esto resulta útil si estás creando una aplicación extremadamente sensible a la latencia que necesita realizar varias operaciones de adición de tareas a diferentes colas al mismo tiempo.

Si quieres hacer llamadas asíncronas a una cola de tareas, usa los métodos asíncronos que proporciona la clase Queue. Llama a get en el Future devuelto para forzar la finalización de la solicitud. Cuando añadas tareas de forma asíncrona en una transacción, debes llamar a get() en Future antes de confirmar la transacción para asegurarte de que la solicitud se ha completado.

Poner tareas en cola en transacciones de Cloud Datastore

Puedes poner en cola una tarea como parte de una transacción de Datastore, de forma que la tarea solo se ponga en cola (y se garantice que se ponga en cola) si la transacción se confirma correctamente. Las tareas añadidas en una transacción se consideran parte de ella y tienen el mismo nivel de aislamiento y coherencia.

Una aplicación no puede insertar más de cinco tareas transaccionales en colas de tareas durante una sola transacción. Las tareas de transacción no deben tener nombres especificados por el usuario.

En el siguiente ejemplo de código se muestra cómo insertar tareas transaccionales en una cola de inserción como parte de una transacción de 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 DeferredTasks en lugar de un servicio de trabajador

Configurar un controlador para cada tarea distinta (como se describe en las secciones anteriores) puede ser engorroso, al igual que serializar y deserializar argumentos complejos para la tarea, sobre todo si tienes muchas tareas diversas pero pequeñas que quieres ejecutar en la cola. El SDK de Java incluye una interfaz llamada DeferredTask. Esta interfaz te permite definir una tarea como un solo método. Esta interfaz usa la serialización de Java para empaquetar una unidad de trabajo en una cola de tareas. Una simple devolución de dicho método se considera un éxito. Si se produce alguna excepción en ese método, se considera un error.


/** 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!");
}

Trabajar con tareas en una aplicación multicliente

De forma predeterminada, las colas de inserción usan el espacio de nombres actual tal como se define en el gestor de espacios de nombres en el momento en que se crea la tarea. Si tu aplicación usa multitenancy, consulta la API Java 8 de espacios de nombres.

Siguientes pasos