Scenario di esempio - 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à.

Scenario di esempio

Esiste una classe di modifiche del codice che è considerata non retrocompatibile. Queste modifiche includono gli aggiornamenti che modificano il numero, il tipo o l'ordine dei task pianificati. Analizza l'esempio seguente:

Scrivi un codice del decisore per pianificare due task di timer. Avvii un'esecuzione ed esegui una decisione. Come risultato, vengono pianificati due task di timer, con ID 1 e 2.

Se aggiorni il codice del decisore per pianificare un solo timer prima che venga eseguita la decisione successiva, nel prossimo task di decisione il framework non riuscirà a riprodurre il secondo evento TimerFired, perché l'ID 2 non corrisponde a nessun task di timer prodotto dal codice.

Struttura dello scenario

La struttura seguente mostra le fasi di questo scenario. L'obiettivo finale dello scenario è quello di effettuare la migrazione a un sistema che pianifichi solo un timer ma che non comprometta le esecuzioni avviate prima della migrazione.

  1. La versione iniziale del decisore

    1. Scrivi il decisore.

    2. Avvia il decisore.

    3. Il decisore pianifica due timer.

    4. Il decisore avvia cinque esecuzioni.

    5. Arresta il decisore.

  2. Una modifica del decisore non retrocompatibile

    1. Modifica il decisore.

    2. Avvia il decisore.

    3. Il decisore pianifica un timer.

    4. Il decisore avvia cinque esecuzioni.

La seguente sezione include esempi di un codice Java che mostrano come implementare questo scenario. Gli esempi di codice nella sezione Soluzioni mostrano vari modi per correggere le modifiche non retrocompatibili.

Nota

Puoi utilizzare la versione più recente di AWS SDK for Java per eseguire questo codice.

Codice comune

Il seguente codice Java non cambia tra gli esempi di questo scenario.

SampleBase.java

package sample; import java.util.ArrayList; import java.util.List; import java.util.UUID; import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow; import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClientBuilder; import com.amazonaws.services.simpleworkflow.flow.JsonDataConverter; import com.amazonaws.services.simpleworkflow.model.DescribeWorkflowExecutionRequest; import com.amazonaws.services.simpleworkflow.model.DomainAlreadyExistsException; import com.amazonaws.services.simpleworkflow.model.RegisterDomainRequest; import com.amazonaws.services.simpleworkflow.model.Run; import com.amazonaws.services.simpleworkflow.model.StartWorkflowExecutionRequest; import com.amazonaws.services.simpleworkflow.model.TaskList; import com.amazonaws.services.simpleworkflow.model.WorkflowExecution; import com.amazonaws.services.simpleworkflow.model.WorkflowExecutionDetail; import com.amazonaws.services.simpleworkflow.model.WorkflowType; public class SampleBase { protected String domain = "DeciderChangeSample"; protected String taskList = "DeciderChangeSample-" + UUID.randomUUID().toString(); protected AmazonSimpleWorkflow service = AmazonSimpleWorkflowClientBuilder.defaultClient(); { try { AmazonSimpleWorkflowClientBuilder.defaultClient().registerDomain(new RegisterDomainRequest().withName(domain).withDescription("desc").withWorkflowExecutionRetentionPeriodInDays("7")); } catch (DomainAlreadyExistsException e) { } } protected List<WorkflowExecution> workflowExecutions = new ArrayList<>(); protected void startFiveExecutions(String workflow, String version, Object input) { for (int i = 0; i < 5; i++) { String id = UUID.randomUUID().toString(); Run startWorkflowExecution = service.startWorkflowExecution( new StartWorkflowExecutionRequest().withDomain(domain).withTaskList(new TaskList().withName(taskList)).withInput(new JsonDataConverter().toData(new Object[] { input })).withWorkflowId(id).withWorkflowType(new WorkflowType().withName(workflow).withVersion(version))); workflowExecutions.add(new WorkflowExecution().withWorkflowId(id).withRunId(startWorkflowExecution.getRunId())); sleep(1000); } } protected void printExecutionResults() { waitForExecutionsToClose(); System.out.println("\nResults:"); for (WorkflowExecution wid : workflowExecutions) { WorkflowExecutionDetail details = service.describeWorkflowExecution(new DescribeWorkflowExecutionRequest().withDomain(domain).withExecution(wid)); System.out.println(wid.getWorkflowId() + " " + details.getExecutionInfo().getCloseStatus()); } } protected void waitForExecutionsToClose() { loop: while (true) { for (WorkflowExecution wid : workflowExecutions) { WorkflowExecutionDetail details = service.describeWorkflowExecution(new DescribeWorkflowExecutionRequest().withDomain(domain).withExecution(wid)); if ("OPEN".equals(details.getExecutionInfo().getExecutionStatus())) { sleep(1000); continue loop; } } return; } } protected void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }

Input.java

package sample; public class Input { private Boolean skipSecondTimer; public Input() { } public Input(Boolean skipSecondTimer) { this.skipSecondTimer = skipSecondTimer; } public Boolean getSkipSecondTimer() { return skipSecondTimer != null && skipSecondTimer; } public Input setSkipSecondTimer(Boolean skipSecondTimer) { this.skipSecondTimer = skipSecondTimer; return this; } }

Scrivere il codice iniziale del decisore

Di seguito è riportato il codice Java iniziale del decisore. Viene registrato come versione 1 e pianifica due task di timer da cinque secondi.

InitialDecider.java

package sample.v1; import com.amazonaws.services.simpleworkflow.flow.DecisionContext; import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl; import com.amazonaws.services.simpleworkflow.flow.WorkflowClock; import com.amazonaws.services.simpleworkflow.flow.annotations.Execute; import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow; import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions; import sample.Input; @Workflow @WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5) public interface Foo { @Execute(version = "1") public void sample(Input input); public static class Impl implements Foo { private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext(); private WorkflowClock clock = decisionContext.getWorkflowClock(); @Override public void sample(Input input) { System.out.println("Decision (V1) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); clock.createTimer(5); } } }

Simulazione di una modifica non retrocompatibile

Il seguente codice Java modificato del decisore è un buon esempio di modifica non retrocompatibile. Il codice è ancora registrato come versione 1, ma pianifica solo un timer.

ModifiedDecider.java

package sample.v1.modified; import com.amazonaws.services.simpleworkflow.flow.DecisionContext; import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl; import com.amazonaws.services.simpleworkflow.flow.WorkflowClock; import com.amazonaws.services.simpleworkflow.flow.annotations.Execute; import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow; import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions; import sample.Input; @Workflow @WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5) public interface Foo { @Execute(version = "1") public void sample(Input input); public static class Impl implements Foo { private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext(); private WorkflowClock clock = decisionContext.getWorkflowClock(); @Override public void sample(Input input) { System.out.println("Decision (V1 modified) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); } } }

Il seguente codice Java ti permette di simulare il problema di apportare modifiche non retrocompatibili eseguendo il decisore modificato.

RunModifiedDecider.java

package sample; import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker; public class BadChange extends SampleBase { public static void main(String[] args) throws Exception { new BadChange().run(); } public void run() throws Exception { // Start the first version of the decider WorkflowWorker before = new WorkflowWorker(service, domain, taskList); before.addWorkflowImplementationType(sample.v1.Foo.Impl.class); before.start(); // Start a few executions startFiveExecutions("Foo.sample", "1", new Input()); // Stop the first decider worker and wait a few seconds // for its pending pollers to match and return before.suspendPolling(); sleep(2000); // At this point, three executions are still open, with more decisions to make // Start the modified version of the decider WorkflowWorker after = new WorkflowWorker(service, domain, taskList); after.addWorkflowImplementationType(sample.v1.modified.Foo.Impl.class); after.start(); // Start a few more executions startFiveExecutions("Foo.sample", "1", new Input()); printExecutionResults(); } }

Quando esegui il programma, le tre esecuzioni che non vanno a buon fine sono quelle avviate secondo la versione iniziale del decisore e proseguite dopo la migrazione.