Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Entre bastidores
Tarea
El tipo primitivo subyacente que AWS Flow Framework para Java utiliza para administrar la ejecución de código asíncrono es la clase Task
. Un objeto tipo Task
representa trabajo que hay que realizar de manera asíncrona. Cuando llama a un método asíncrono, el marco de trabajo crea una Task
para ejecutar el código en ese método y lo pone en una lista para su ejecución más adelante. De manera parecida, al invocar una Activity
, se crea una Task
para dicha actividad. La llamada al método regresa después de esto, devolviendo normalmente una Promise<T>
como resultado futuro de la llamada.
La clase Task
es pública y puede usarse directamente. Por ejemplo, podemos volver a escribir el ejemplo de Hello World para que use una Task
en lugar de un método asíncrono.
@Override public void startHelloWorld(){ final Promise<String> greeting = client.getName(); new Task(greeting) { @Override protected void doExecute() throws Throwable { client.printGreeting("Hello " + greeting.get() +"!"); } }; }
El marco de trabajo llama al método doExecute()
cuando todas las Promise
s que se han pasado al constructor de las Task
están listas. Para obtener más información acerca de la clase Task
, consulte la documentación de AWS SDK for Java.
El marco de trabajo también incluye una clase llamada Functor
que representa una Task
que es también una Promise<T>
. El objeto Functor
está listo cuando se completa la Task
. En el siguiente ejemplo, se crea un Functor
para obtener el mensaje de saludo:
Promise<String> greeting = new Functor<String>() { @Override protected Promise<String> doExecute() throws Throwable { return client.getGreeting(); } }; client.printGreeting(greeting);
Orden de ejecución
Las tareas se vuelven elegibles para la ejecución solo cuando todos los parámetros escritos Promise<T>
, que se han pasado a la actividad o al método asíncrono correspondientes, están listos. Una Task
que está lista para la ejecución se mueve de manera lógica a una cola lista. En otras palabras, se programa para la ejecución. Para ejecutar la tarea, la clase de proceso de trabajo invoca el código que se escribió en el cuerpo del método asíncrono o programa una tarea de actividad en Amazon Simple Workflow Service (AWS) en el caso de un método de actividad.
A medida que las tareas ejecutan y producen resultados, hacen que otras tareas estén preparadas y la ejecución del programa continúa avanzando. La manera en que el marco de trabajo ejecuta tareas es importante para comprender el orden en el que se ejecuta su código asíncrono. El código que aparece secuencialmente en su programa podría no ejecutarse en ese orden.
Promise<String> name = getUserName(); printHelloName(name); printHelloWorld(); System.out.println("Hello, Amazon!"); @Asynchronous private Promise<String> getUserName(){ return Promise.asPromise("Bob"); } @Asynchronous private void printHelloName(Promise<String> name){ System.out.println("Hello, " + name.get() + "!"); } @Asynchronous private void printHelloWorld(){ System.out.println("Hello, World!"); }
El código en la lista de arriba imprimirá lo siguiente:
Hello, Amazon! Hello, World! Hello, Bob
Quizás no sea lo que esperaba pero puede explicarse fácilmente analizando cómo se ejecutaron las tareas para los métodos asíncronos:
-
La llamada a
getUserName
crea unaTask
. La llamaremosTask1
. Dado quegetUserName
no toma ningún parámetro, se pone inmediatamente aTask1
en la cola lista. -
A continuación, la llamada a
printHelloName
crea unaTask
que tiene que esperar el resultado degetUserName
. La llamaremosTask2
. Dado que el valor requisito aun no está listo, se poneTask2
en la lista de espera. -
A continuación se crea una tarea para
printHelloWorld
y se añade a la cola lista. La llamaremosTask3
. -
A continuación, la instrucción
println
imprime "Hello, Amazon!" en la consola. -
En este punto,
Task1
yTask3
están en la cola lista yTask2
está en la lista de espera. -
El proceso de trabajo ejecuta
Task1
y su resultado hace queTask2
esté listo.Task2
se añade a la cola lista por detrás deTask3
. -
Task3
yTask2
se ejecutan, a continuación, en ese orden.
La ejecución de actividades sigue el mismo patrón. Cuando se llama a un método en el cliente de actividad, este crea una Task
que, cuando se ejecuta, programa una actividad en Amazon SWF.
El marco de trabajo confía en características como la generación de códigos y proxies dinámicos para inyectar la lógica para la conversión de llamadas de método en invocaciones de actividad y tareas asíncronas en su programa.
Ejecución del flujo de trabajo
La clase de proceso de trabajo también administra la ejecución de la implementación de flujo de trabajo. Cuando se llama a un método en el cliente de flujo de trabajo, llama a Amazon SWF para crear una instancia de flujo de trabajo. Las tareas de Amazon SWF no deben confundirse con las tareas del marco de trabajo. Una tarea en Amazon SWF es o bien una tarea de actividad o una tarea de decisión. La ejecución de las tareas de actividad es sencilla. La clase de proceso de trabajo de actividad recibe tareas de actividad de Amazon SWF, invoca el método de actividad apropiado de la implementación y devuelve el resultado a Amazon SWF.
La ejecución de las tareas de decisión requiere más pasos. El proceso de trabajo de flujo de trabajo recibe las tareas de decisión de Amazon SWF. Una tarea de decisión es en realidad una solicitud que pregunta a la lógica de flujo de trabajo qué debe hacer a continuación. La primera tarea de decisión se genera para una instancia de flujo de trabajo cuando se inicia a través del cliente de flujo de trabajo. Al recibir esta tarea de decisión, el marco de trabajo comienza a ejecutar el código en el método de flujo de trabajo anotado con @Execute
. Este método ejecuta la lógica de coordinación que programa actividades. Cuando el estado de la instancia de flujo de trabajo cambia, por ejemplo, cuando se completa una actividad, se programan tareas de decisión adicionales. En este punto, la lógica de flujo de trabajo puede decidir actuar en función del resultado de la actividad; por ejemplo, podría decidir programar otra actividad.
El marco de trabajo oculta todos estos detalles al desarrollador traduciendo a la perfección tareas de decisión a la lógica del flujo de trabajo. Desde el punto de vista de un desarrollador, el código tiene el mismo aspecto que un programa normal. Internamente, el marco de trabajo lo asigna a llamadas a Amazon SWF y a tareas de decisión mediante el historial que mantiene Amazon SWF. Cuando llega una tarea de decisión, el marco de trabajo reproduce la ejecución del programa incorporando los resultados de las actividades completadas hasta el momento. Las actividades y métodos asíncronos que estaban esperando estos resultados se desbloquean y la ejecución del programa avanza.
En la siguiente tabla se muestra la ejecución del flujo de trabajo de procesamiento de imágenes de ejemplo y el historial correspondiente.
La ejecución del flujo de trabajo de miniaturas | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
La ejecución del programa de flujo de trabajo | El historial que mantiene Amazon SWF | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ejecución inicial | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reproducir | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reproducir | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reproducir | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
Cuando se realiza una llamada a processImage
, el marco de trabajo crea una nueva instancia de flujo de trabajo en Amazon SWF. Se trata de un registro duradero de la instancia de flujo de trabajo que se está iniciando. El programa se ejecuta hasta la llamada a la actividad downloadImage
que pide a Amazon SWF que programe una actividad. El flujo de trabajo se sigue ejecutando y crea tareas para las actividades posteriores, pero no se pueden ejecutar hasta que la actividad downloadImage
se complete; por lo tanto, este episodio de reproducción finaliza. Amazon SWF envía la tarea de la actividad downloadImage
para que se ejecute y, una vez finalizada, se registra en el historial junto con el resultado. El flujo de trabajo está ahora listo para avanzar y Amazon SWF genera una tarea de decisión. El marco de trabajo recibe la tarea de decisión y reproduce el flujo de trabajo incorporando el resultado de las imágenes descargadas tal y como está registrado en el historial. Esto desbloquea la tarea de createThumbnail
, y la ejecución del programa continúa avanzando con la programación de la tarea de actividad createThumbnail
en Amazon SWF. Se repite el mismo proceso para uploadImage
. La ejecución del programa sigue su curso hasta que el flujo de trabajo ha procesado todas las imágenes y no hay tareas pendientes. Dado que no se almacena localmente ningún estado de ejecución, cada tarea de decisión podría ejecutarse potencialmente en una máquina diferente. Esto le permite escribir fácilmente programas que sean tolerantes a errores y fácilmente escalables.
No determinismo
Dado que el marco de trabajo confía en la reproducción, es importante que el código de orquestación (todo el código de flujo de trabajo con la excepción de las implementaciones de actividad) sea determinista. Por ejemplo, el flujo de control en su programa no debería depender de un número aleatorio o de la hora actual. Dado que estas cosas cambiarán entre invocaciones, la reproducción podría no seguir la misma ruta a través de la lógica de orquestación. Esto llevará a errores o resultados imprevistos. El marco de trabajo proporciona un WorkflowClock
que puede utilizar para obtener la hora actual de manera determinista. Consulte la sección en Contexto de ejecución para obtener más información.
nota
Una conexión de Spring incorrecta de objetos de implementación de flujo de trabajo también puede llevar al no determinismo. Los beans de implementación de flujo de trabajo así como los bean de los que dependen tienen que estar dentro del alcance del flujo de trabajo (WorkflowScope
). Por ejemplo, la conexión de un bean de implementación de flujo de trabajo a un bean que mantiene estado y está dentro del contexto global dará como resultado un comportamiento inesperado. Consulte la sección Integración con Spring para obtener más información.