本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
故障排除和调试提示
本节介绍您在开发用 AWS Flow Framework 于 Java 的工作流程时可能会遇到的一些常见陷阱。它还提供一些提示来帮助您诊断和调试问题。
编译错误
如果您使用 AspectJ 编译时编织选项,则可能会遇到编译器无法找到为您的工作流和活动生成的客户端类的编译时错误。此类编译错误的可能原因是 AspectJ 生成器忽略了在编译期间生成的客户端。解决此问题的方法是从项目中删除 AspectJ 功能,然后重新启用该功能。请注意,每次您的工作流或活动接口更改时,您都需要执行此操作。由于此问题,建议您改用加载时编织选项。有关更多详细信息,请参阅设置适用于 Java 的 AWS Flow Framework 一节。
未知资源错误
当您尝试对不可用的资源执行操作时,Amazon SWF 会返回未知资源错误。此错误的常见原因是:
-
您配置的工作线程的域不存在。要解决这个问题,请先使用 Amazon SWF 控制台或 Amazon SWF 服务 API 注册域。
-
您尝试创建的工作流执行或活动任务的类型尚未注册。如果您在工作线程运行之前尝试创建工作流执行,则会发生此情况。由于工作线程在首次运行时注册其类型,您必须在尝试开始执行之前至少运行它们一次 (或使用控制台或服务 API 手动注册类型)。请注意,在注册类型之后,您便可以创建执行,即使没有工作线程在运行也是如此。
-
工作线程尝试完成的任务已超时。例如,如果工作人员处理任务的时间太长并且超过了超时时间,那么当它尝试完成任务或任务失败时,它就会出 UnknownResource 错。 AWS Flow Framework 工作人员将继续对 Amazon SWF 进行民意调查,并处理其他任务。但是,您应考虑调整超时。调整超时需要您注册新版本的活动类型。
对 Promise 调用 get() 时出现异常
与 Java Future 不同,Promise
是非阻塞构造,对尚未就绪的 Promise
调用 get()
会引发异常而非阻塞。使用 Promise
的正确方法是将其传递给异步方法(或任务),并在相应异步方法中访问其值。 AWS Flow Framework 可确保仅在传递到异步方法的所有 Promise
参数都已就绪时才调用异步方法。如果您认为自己的代码是正确的,或者在运行其中一个 AWS Flow Framework 示例时遇到了这个问题,那么很可能是由于AspectJ的配置不正确。有关详细信息,请参阅设置适用于 Java 的 AWS Flow Framework 一节。
非确定性工作流
正如不确定性一节所述,工作流的实现必须是确定性的。可导致非确定性的一些常见错误是使用系统时钟、使用随机数和生成 GUID。由于这些构造可能在不同的时间返回不同的值,工作流的控制流可能在每次执行时使用不同的路径 (有关详细信息,请参阅 AWS Flow Framework 基本概念:分布式执行和深入剖析)。如果框架在执行工作流时检测到非确定性,则将引发异常。
因版本控制而出现问题
在您实现工作流或活动的新版本时(例如添加新功能时),应使用适当的注解来增加该类型的版本:@Workflow、@Activites 或 @Activity。在部署工作流的新版本时,您通常具有已在运行的现有版本的执行。因此,您需要确保使用工作流和活动的适当版本的工作线程获得任务。您可以对每个版本使用一组不同的任务列表来实现此目的。例如,您可以向任务列表名称附加版本号。这确保将属于工作流和活动的不同版本的任务分配给适当的工作线程。
对工作流执行进行故障排除和调试
对工作流执行进行故障排除的第一步是使用 Amazon SWF 控制台来查看工作流历史记录。工作流历史记录是更改工作流执行的执行状态的所有事件的完整的权威记录。此历史记录由 Amazon SWF 维护,对于诊断问题来说非常重要。利用 Amazon SWF 控制台,您可以搜索工作流执行并深入了解各个历史记录事件。
AWS Flow Framework 提供了一个WorkflowReplayer
类,您可以使用该类在本地重播工作流程执行并对其进行调试。使用此课程,您可以调试已关闭和正在运行的工作流执行。WorkflowReplayer
依赖存储在 Amazon SWF 中的历史记录来执行回放。您可以将其指向您 Amazon SWF 账户中的工作流执行,或向其提供历史记录事件(例如,您可以从 Amazon SWF 中检索历史记录,并在本地对其进行序列化以供以后使用)。在您使用 WorkflowReplayer
重播工作流执行时,这不会影响您的账户中正在运行的工作流执行。重播完全在客户端上进行。您可以像往常一样使用调试工具来调试工作流、创建断点和进入代码。如果您使用的是 Eclipse,请考虑添加步骤过滤器来筛选 AWS Flow Framework 软件包。
例如,以下代码段可用于重播工作流执行:
String workflowId = "testWorkflow"; String runId = "<run id>"; Class<HelloWorldImpl> workflowImplementationType = HelloWorldImpl.class; WorkflowExecution workflowExecution = new WorkflowExecution(); workflowExecution.setWorkflowId(workflowId); workflowExecution.setRunId(runId); WorkflowReplayer<HelloWorldImpl> replayer = new WorkflowReplayer<HelloWorldImpl>( swfService, domain, workflowExecution, workflowImplementationType); System.out.println("Beginning workflow replay for " + workflowExecution); Object workflow = replayer.loadWorkflow(); System.out.println("Workflow implementation object:"); System.out.println(workflow); System.out.println("Done workflow replay for " + workflowExecution);
AWS Flow Framework 还允许您获取工作流程执行的异步线程转储。此线程转储可为您提供所有已打开的异步任务的调用堆栈。此信息可用于确定执行中的哪些任务正在等待处理并可能被卡住。例如:
String workflowId = "testWorkflow"; String runId = "<run id>"; Class<HelloWorldImpl> workflowImplementationType = HelloWorldImpl.class; WorkflowExecution workflowExecution = new WorkflowExecution(); workflowExecution.setWorkflowId(workflowId); workflowExecution.setRunId(runId); WorkflowReplayer<HelloWorldImpl> replayer = new WorkflowReplayer<HelloWorldImpl>( swfService, domain, workflowExecution, workflowImplementationType); try { String flowThreadDump = replayer.getAsynchronousThreadDumpAsString(); System.out.println("Workflow asynchronous thread dump:"); System.out.println(flowThreadDump); } catch (WorkflowException e) { System.out.println("No asynchronous thread dump available as workflow has failed: " + e); }
任务丢失
有时,您可能关闭了工作线程,然后紧接着启动了新工作线程,但是发现任务被传输给了旧工作线程。这可能因分布在多个进程中的系统中的争用条件而发生。在紧密循环中运行单元测试时,也可能会出现此问题。在 Eclipse 中停止测试有时也会引发此问题,因为可能没有调用关闭处理程序。
要确定此问题实际上是由于旧工作线程获得任务,您应查看工作流历史记录来确定哪个进程获得了应由新工作线程获得的任务。例如,历史记录中的 DecisionTaskStarted
事件包含获得任务的工作流工作线程的标识。Flow Framework 使用的 ID 的格式为:{processId
}@
{host name
}。例如,以下是 Amazon SWF 控制台中示例执行的 DecisionTaskStarted
事件的详细信息:
事件时间戳 |
Mon Feb 20 11:52:40 GMT-800 2012 |
求同 |
2276@ip-0A6C1DF5 |
预定事件 ID |
33 |
为了避免出现此情况,请对每个测试使用不同的任务列表。另外,请考虑在关闭旧工作线程和启动新工作线程之间添加延迟。