Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Come si presenta
Processo
La primitiva sottostante che ilAWS Flow FrameworkPer Java utilizza per gestire l'esecuzione del codice asincrono è ilTask
classe. Un oggetto di tipo Task
rappresenta il lavoro che deve essere eseguito in modo asincrono. Quando chiami un metodo asincrono, il framework crea un Task
per eseguire il codice in quel metodo e lo inserisce in un elenco per essere eseguito successivamente. Analogamente, quando richiami Activity
, viene creato un Task
apposito. Dopo questa operazione la chiamata del metodo ritorna, solitamente restituendo un Promise<T>
come risultato futuro della chiamata.
La classe Task
è pubblica e può essere utilizzata direttamente. Ad esempio, possiamo riscrivere l'esempio di Hello World per utilizzare un Task
anziché un metodo asincrono.
@Override public void startHelloWorld(){ final Promise<String> greeting = client.getName(); new Task(greeting) { @Override protected void doExecute() throws Throwable { client.printGreeting("Hello " + greeting.get() +"!"); } }; }
Il framework chiama il metodo doExecute()
quando tutti i Promise
passati al costruttore di Task
sono pronti. Per ulteriori dettagli sulla classe Task
consulta la documentazione AWS SDK for Java.
Il framework include anche una classe chiamata Functor
che rappresenta un Task
che è anche un Promise<T>
. L'oggetto Functor
è pronto quando Task
è completato. Nel seguente esempio, un Functor
viene creato per ottenere il messaggio di saluto:
Promise<String> greeting = new Functor<String>() { @Override protected Promise<String> doExecute() throws Throwable { return client.getGreeting(); } }; client.printGreeting(greeting);
Ordine di esecuzione
I task possono essere eseguiti soltanto quando tutti i parametri Promise<T>
digitati, passati al metodo o all'attività asincroni corrispondenti, sono pronti. Un Task
pronto per l'esecuzione viene logicamente spostato su una coda pronta. In altre parole, è pianificato per l'esecuzione. La classe di lavoratore esegue il task richiamando il codice scritto nel corpo del metodo asincrono o pianificando un task di attività in Amazon Simple Workflow Service (AWS) in caso di metodo di attività.
Man mano che i task vengono eseguiti e producono risultati, altri task sono pronti e l'esecuzione del programma continua il suo ciclo. Il modo in cui il framework esegue i task è importante per comprendere in che ordine viene eseguito il codice asincrono. Il codice che appare in sequenza all'interno del tuo programma potrebbe non essere eseguito in quell'ordine.
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!"); }
Il codice nell'elenco sopra visualizzerà i seguenti dati:
Hello, Amazon! Hello, World! Hello, Bob
Questo non è il risultato che ti aspetti, ma può essere facilmente spiegato pensando al modo in cui vengono eseguiti i task per i metodi asincroni:
-
La chiamata a
getUserName
creaTask
. ChiamiamoloTask1
. Dato chegetUserName
non utilizza i parametri,Task1
viene immediatamente inserito nella coda pronta. -
Successivamente, la chiamata a
printHelloName
creaTask
che deve aspettare il risultato digetUserName
. ChiamiamoloTask2
. Dato che il valore richiesto non è ancora pronto,Task2
viene inserito nell'elenco di attesa. -
In seguito viene creato un task per
printHelloWorld
e aggiunto alla coda pronta. ChiamiamoloTask3
. -
La
println
L'istruzione quindi visualizza «Hello, Amazon!» alla console. -
A questo punto,
Task1
eTask3
sono inseriti nella coda pronta eTask2
nell'elenco di attesa. -
Il lavoratore esegue
Task1
e il risultato rendeTask2
pronto.Task2
viene aggiunto alla coda pronta dietroTask3
. -
Task3
eTask2
vengono poi eseguiti in quell'ordine.
L'esecuzione delle attività segue lo stesso schema. Quando chiami un metodo sul client di attività, viene creato unTask
che una volta eseguito pianifica un'attività in Amazon SWF.
Il framework si basa su caratteristiche come la generazione del codice e i proxy dinamici per immettere la logica che converte le chiamate di metodo in richiami di attività e in task asincroni nel tuo programma.
Esecuzione del flusso di lavoro
L'esecuzione dell'implementazione del flusso di lavoro viene gestita dalla classe di lavoratore. Quando chiami un metodo sul client del flusso di lavoro, viene chiamato Amazon SWF per creare un'istanza del flusso di lavoro. I task in Amazon SWF non devono essere confusi con i task nel framework. Un task in Amazon SWF è un task di attività o un task di decisione. L'esecuzione dei task di attività è semplice. La classe di lavoratore di attività riceve i task di attività da Amazon SWF, richiama il metodo di attività appropriato nell'implementazione e restituisce il risultato ad Amazon SWF.
L'esecuzione dei task di decisione è più complesso. Il lavoratore del flusso di lavoro riceve i task di decisione da Amazon SWF. Un task di decisione è effettivamente una richiesta per sapere dalla logica di flusso di lavoro quali sono i passaggi successivi. Il primo task di decisione viene generato per un'istanza di flusso di lavoro quando viene iniziata sul client di flusso di lavoro. Dopo aver ricevuto questo task di decisione, il framework inizia a eseguire il codice nel metodo di flusso di lavoro annotato con @Execute
. Questo metodo esegue la logica di coordinamento che pianifica le attività. Quando lo stato dell'istanza del flusso di lavoro cambia, ad esempio al completamento di un'attività, vengono pianificate ulteriori attività decisionali. A questo punto, la logica di flusso di lavoro può decidere di eseguire un'azione in base ai risultati dell'attività; ad esempio, potrebbe decidere di pianificare un'altra attività.
Il framework nasconde tutti questi dettagli allo sviluppatore traducendo in modo perfetto i task di decisione nella logica di flusso di lavoro. Dal punto di vista dello sviluppatore, il codice assomiglia a un normale programma. Sotto le copertine, il framework lo mappa alle chiamate a Amazon SWF e ai task di decisione utilizzando la cronologia gestita da Amazon SWF. Quando giunge un task di decisione, il framework riproduce l'esecuzione del programma inserendo i risultati delle attività completate fino a quel momento. I metodi e le attività asincroni che stavano aspettando i risultati vengono sbloccati e l'esecuzione del programma prosegue.
L'esecuzione del flusso di lavoro di elaborazione di immagini e la relativa cronologia vengono mostrate nella seguente tabella.
Esecuzione del programma di flusso di lavoro | Cronologia gestita da Amazon SWF |
---|---|
Esecuzione iniziale | |
|
|
Riproduci di nuovo | |
|
|
Riproduci di nuovo | |
|
|
Riproduci di nuovo | |
|
|
Quando una chiamata aprocessImage
È creato, il framework crea una nuova istanza del flusso di lavoro in Amazon SWF. Rappresenta un record duraturo del momento in cui viene iniziata un'istanza di flusso di lavoro. Il programma è in esecuzione fino alla chiamata aldownloadImage
Un'attività che chiede ad Amazon SWF di pianificare un'attività. Il flusso di lavoro viene eseguito ulteriormente e crea attività per le attività successive, ma non possono essere eseguite fino aldownloadImage
l'attività si completa; quindi, questo episodio di replay termina. Amazon SWF invia l'attività perdownloadImage
Una volta completato, un record viene creato nella cronologia insieme al risultato. Ora il flusso di lavoro è pronto per proseguire e un task di decisione viene generato da Amazon SWF. Il framework riceve il task di decisione e riproduce il flusso di lavoro inserendo il risultato dell'immagine scaricata registrato nella cronologia. Questo sblocca l'attività percreateThumbnail
e l'esecuzione del programma prosegue pianificando ilcreateThumbnail
attività in Amazon SWF. Lo stesso processo si ripete per uploadImage
. L'esecuzione del programma prosegue in questo modo fino a quando il flusso di lavoro ha elaborato tutte le immagini e non ci sono più task in sospeso. Dato che nessun stato di esecuzione viene memorizzato a livello locale, ogni task di decisione può essere potenzialmente eseguito su una macchina diversa. Questa operazione ti permette di scrivere programmi che siano tolleranti ai guasti e facilmente scalabili.
Non determinismo
Dato che il framework si basa sulla riproduzione, è importante che il codice di orchestrazione (tutto il codice di flusso di lavoro a eccezione delle implementazioni di attività) sia deterministico. Ad esempio, il flusso di controllo del programma non deve dipendere da un numero casuale o dall'ora corrente. Dato che questi elementi cambieranno tra i richiami, la riproduzione potrebbe non seguire lo stesso percorso attraverso la logica di orchestrazione. Ciò potrebbe portare a risultati o errori imprevisti. Il framework offre un WorkflowClock
che puoi utilizzare per individuare l'ora corrente in modo deterministico. Per ulteriori informazioni, consulta la sezione su Contesto di esecuzione.
Nota
Il cablaggio Spring non corretto degli oggetti di implementazione del flusso di lavoro può condurre al non determinismo. I bean di implementazione del flusso di lavoro e i bean da cui dipendono devono essere inclusi nell'ambito del flusso di lavoro (WorkflowScope
). Ad esempio, cablare un bean di implementazione del flusso di lavoro a un bean che mantiene il proprio stato e si trova nel contesto globale porterà a un comportamento imprevisto. Per ulteriori informazioni, consulta la sezione Integrazione di Spring.