Come si presenta - AWS Flow Framework per Java

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 è ilTaskclasse. 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:

  1. La chiamata a getUserName crea Task. Chiamiamolo Task1. Dato che getUserName non utilizza i parametri, Task1 viene immediatamente inserito nella coda pronta.

  2. Successivamente, la chiamata a printHelloName crea Task che deve aspettare il risultato di getUserName. Chiamiamolo Task2. Dato che il valore richiesto non è ancora pronto, Task2 viene inserito nell'elenco di attesa.

  3. In seguito viene creato un task per printHelloWorld e aggiunto alla coda pronta. Chiamiamolo Task3.

  4. LaprintlnL'istruzione quindi visualizza «Hello, Amazon!» alla console.

  5. A questo punto, Task1 e Task3 sono inseriti nella coda pronta e Task2 nell'elenco di attesa.

  6. Il lavoratore esegue Task1 e il risultato rende Task2 pronto. Task2 viene aggiunto alla coda pronta dietro Task3.

  7. Task3 e Task2 vengono poi eseguiti in quell'ordine.

L'esecuzione delle attività segue lo stesso schema. Quando chiami un metodo sul client di attività, viene creato unTaskche 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 flusso di lavoro di anteprima
Esecuzione del programma di flusso di lavoro Cronologia gestita da Amazon SWF
Esecuzione iniziale
  1. Invia loop

  2. getImageUrls

  3. downloadImage

  4. createThumbnail (task nella coda di attesa)

  5. uploadImage (task nella coda di attesa)

  6. <prossima iterazione del loop>

  1. Avvio dell'istanza di flusso di lavoro, id="1"

  2. downloadImage pianificato

Riproduci di nuovo
  1. Invia loop

  2. getImageUrls

  3. percorso downloadImage image ="foo"

  4. createThumbnail

  5. uploadImage (task nella coda di attesa)

  6. <prossima iterazione del loop>

  1. Avvio dell'istanza di flusso di lavoro, id="1"

  2. downloadImage pianificato

  3. downloadImage completato, restituisce="foo"

  4. createThumbnail pianificato

Riproduci di nuovo
  1. Invia loop

  2. getImageUrls

  3. percorso downloadImage image ="foo"

  4. percorso miniatura createThumbnail="bar"

  5. uploadImage

  6. <prossima iterazione del loop>

  1. Avvio dell'istanza di flusso di lavoro, id="1"

  2. downloadImage pianificato

  3. downloadImage completato, restituisce="foo"

  4. createThumbnail pianificato

  5. createThumbnail completato, restituisce="bar"

  6. uploadImage pianificato

Riproduci di nuovo
  1. Invia loop

  2. getImageUrls

  3. percorso downloadImage image ="foo"

  4. percorso miniatura createThumbnail="bar"

  5. uploadImage

  6. <prossima iterazione del loop>

  1. Avvio dell'istanza di flusso di lavoro, id="1"

  2. downloadImage pianificato

  3. downloadImage completato, restituisce="foo"

  4. createThumbnail pianificato

  5. createThumbnail completato, restituisce="bar"

  6. uploadImage pianificato

  7. uploadImage completato

    ...

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 aldownloadImageUn'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 aldownloadImagel'attività si completa; quindi, questo episodio di replay termina. Amazon SWF invia l'attività perdownloadImageUna 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à percreateThumbnaile l'esecuzione del programma prosegue pianificando ilcreateThumbnailattività 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.