本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
Solutions
您可以使用以下解决方案以避免不向后兼容的更改。有关更多信息,请参阅对决策程序代码进行更改和示例方案。
使用版本控制
在该解决方案中,您将决策程序复制到一个新类,修改决策程序,然后在新工作流程版本中注册决策程序。
VersionedDecider.java
package sample.v2; 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 = "2") 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 (V2) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); } } }
在更新的 Java 代码中,第二个决策程序工作线程运行两个版本的工作流程以允许动态执行继续运行,而与版本 2
中的更改无关。
RunVersionedDecider.java
package sample; import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker; public class VersionedChange extends SampleBase { public static void main(String[] args) throws Exception { new VersionedChange().run(); } public void run() throws Exception { // Start the first version of the decider, with workflow version 1 WorkflowWorker before = new WorkflowWorker(service, domain, taskList); before.addWorkflowImplementationType(sample.v1.Foo.Impl.class); before.start(); // Start a few executions with version 1 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 a worker with both the previous version of the decider (workflow version 1) // and the modified code (workflow version 2) WorkflowWorker after = new WorkflowWorker(service, domain, taskList); after.addWorkflowImplementationType(sample.v1.Foo.Impl.class); after.addWorkflowImplementationType(sample.v2.Foo.Impl.class); after.start(); // Start a few more executions with version 2 startFiveExecutions("Foo.sample", "2", new Input()); printExecutionResults(); } }
在运行该程序时,将成功完成所有执行。
使用功能标志
向后兼容性问题的另一个解决方案是,创建代码分支以在同一类中支持两个实现,以根据输入数据执行分支,而不是根据工作流程版本。
在使用该方法时,每次引入敏感的更改时,您在输入对象中添加一些字段 (或修改现有的字段)。对于在迁移之前启动的执行,输入对象不包含字段 (或具有不同的值)。因此,您不必增加版本号。
注意
如果添加新的字段,请确保 JSON 反序列化过程向后兼容。在迁移后,在引入字段之前序列化的对象仍会成功反序列化。由于在每次缺少字段时 JSON 都会设置 null
值,请始终使用装箱类型 (Boolean
而不是 boolean
) 并处理值为 null
的情况。
FeatureFlagDecider.java
package sample.v1.featureflag; 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 feature flag) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); if (!input.getSkipSecondTimer()) { clock.createTimer(5); } } } }
在更新的 Java 代码中,仍会为版本 1
注册两个版本的工作流程的代码。不过,在迁移后,新执行启动并将输入数据的 skipSecondTimer
字段设置为 true
。
RunFeatureFlagDecider.java
package sample; import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker; public class FeatureFlagChange extends SampleBase { public static void main(String[] args) throws Exception { new FeatureFlagChange().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 a new version of the decider that introduces a change // while preserving backwards compatibility based on input fields WorkflowWorker after = new WorkflowWorker(service, domain, taskList); after.addWorkflowImplementationType(sample.v1.featureflag.Foo.Impl.class); after.start(); // Start a few more executions and enable the new feature through the input data startFiveExecutions("Foo.sample", "1", new Input().setSkipSecondTimer(true)); printExecutionResults(); } }
在运行该程序时,将成功完成所有执行。