Step Functions 中的错误处理 - Amazon Step Functions
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

Step Functions 中的错误处理

PassWait 状态之外的所有状态都可能遇到运行时错误。有多种原因可导致错误发生,例如以下示例:

  • 状态机定义问题(例如,Choice 状态中没有匹配规则)

  • 任务失败(例如,Amazon Lambda 函数中的异常)

  • 临时性问题(例如,网络分区事件)

默认情况下,当某个状态报告错误时,Amazon Step Functions 会导致执行完全失败。

提示

要在 Amazon Web Services 账户中部署包含错误处理的工作流示例,请参阅《Amazon Step Functions 研讨会》模块 8 - 错误处理

错误名称

Step Functions 使用区分大小写的字符串(称为错误名称)来识别 Amazon States Language 中的错误。Amazon States Language 定义了一组内置字符串用于命名广为人知的错误,均以 States. 前缀开头。

States.ALL

一个与任何已知错误名称匹配的通配符。

注意

此错误类型无法捕获 States.DataLimitExceeded 终端错误类型和运行时错误类型。有关这些错误类型的更多信息,请参见 States.DataLimitExceededStates.Runtime

States.DataLimitExceeded

Step Functions 会在以下条件下报告 States.DataLimitExceeded 异常:

  • 当连接器的输出大于有效负载大小配额时。

  • 当状态的输出大于有效负载大小配额时。

  • Parameters 处理后,状态的输入大于有效负载大小配额时。

有关配额的更多信息,请参阅限额

注意

这是一个无法被 States.ALL 错误类型捕获的终端错误。

States.ExceedToleratedFailureThreshold

Map状 态失败是因为失败项目的数量超过了状态机定义中指定的阈值。有关更多信息,请参阅分布式 Map 状态容许的故障阈值

States.HeartbeatTimeout

Task 状态未能发送检测信号的时间超过 HeartbeatSeconds 值。

注意

此错误仅出现在 CatchRetry 字段中。

States.IntrinsicFailure

尝试在有效负载模板中调用内置函数失败。

States.ItemReaderFailed

Map 状态因无法读取 ItemReader 字段中指定的项目来源而失败。有关更多信息,请参阅ItemReader

States.NoChoiceMatched

Choice 状态未能将输入与选项规则中定义的条件相匹配,并且未指定默认过渡。

States.ParameterPathFailure

尝试替换状态 Parameters 字段中名称以路径结尾的 .$ 字段失败。

States.Permissions

Task 状态因没有足够的权限运行指定代码而失败。

States.ResultPathMatchFailure

Step Functions 未能将状态的 ResultPath 字段应用于该状态接收到的输入。

States.ResultWriterFailed

Map 状态因其无法将结果写入 ResultWriter 字段中指定的目的地而失败。有关更多信息,请参阅ResultWriter

States.Runtime

执行因某些无法处理的异常而失败。这些通常是由运行时的错误引起的,例如尝试在 null JSON 有效负载上应用 InputPathOutputPathStates.Runtime 错误不可检索,并且始终会导致执行失败。在 States.ALL 上重试或捕获不会捕获 States.Runtime 错误。

States.TaskFailed

一个在执行期间失败的 Task 状态。在 retry 或 catch 中使用时,States.TaskFailed 充当通配符,可匹配除 States.Timeout 之外的任何已知错误名称。

States.Timeout

一个 Task 状态,该状态要么运行时间长度超过 TimeoutSeconds 值,要么在超过 HeartbeatSeconds 值的时段长度中未能发送检测信号。

此外,如果状态机的运行时间超过指定 TimeoutSeconds 值,则执行失败并显示 States.Timeout 错误。

状态可以报告具有其他名称的错误。但是,这些错误名称不能以 States. 前缀开头。

作为最佳实操,确保生产代码可以处理 Amazon Lambda 服务异常(Lambda.ServiceExceptionLambda.SdkClientException)。有关更多信息,请参阅处理 Lambda 服务异常

注意

Lambda 中未处理的错误在错误输出中报告为 Lambda.Unknown。这些错误包括内存不足错误和函数超时。您可以匹配 Lambda.UnknownStates.ALLStates.TaskFailed 来处理这些错误。当 Lambda 达到最大调用次数时,会出现 Lambda.TooManyRequestsException 错误。有关 Lambda 函数错误的更多信息,请参阅《Amazon Lambda 开发者指南》中的错误处理和自动重试

出错后重试

TaskParallelMap 状态可以有名为 Retry 的字段,其值必须是称为重试器 的对象数组。单个重试器表示特定的重试次数,通常是以递增的时间间隔。

当其中一个状态报告错误并且有一个 Retry 字段时,Step Functions 会按照数组中列出的顺序扫描重试器。当错误名称出现在某个重试器的 ErrorEquals 字段中时,状态机就会按照 Retry 字段中的定义进行重试。

如果redriven执行重新运行了已定义重试的 TaskParallel内联 Map 状态,这些状态的重试尝试计数将重置为 0,以便允许redrive上的最大尝试次数。对于redriven执行,您可以使用控制台跟踪这些状态的单个重试尝试。有关更多信息,请参阅Redriving执行中的redriven执行的重试行为

重试器包含以下字段:

注意

重试被视为一种状态转换。有关状态转换如何影响计费的信息,请参阅 Step Functions 定价

ErrorEquals(必填)

一个匹配错误名称的非空字符串数组。当状态报告错误时,Step Functions 会全面扫描重试器。当错误名称出现在此数组中时,它实施该重试器描述的重试策略。

IntervalSeconds(可选)

一个正整数,表示第一次重试尝试之前等待的秒数(默认值为 1)。IntervalSeconds 的最大值为 99999999。

MaxAttempts(可选)

一个正整数,表示重试的最大次数 (默认值为 3)。如果错误重复发生超过指定次数,则停止重试并恢复正常错误处理。值为 0 表示永不重试错误。 MaxAttempts 的最大值为 99999999。

BackoffRate(可选)

每次重试后,IntervalSeconds 所表示的重试间隔增加的倍数。默认情况下,BackoffRate 值会增加 2.0

例如,假设 IntervalSeconds 为 3,MaxAttempts 为 3,BackoffRate 为 2。第一次重试尝试在错误发生三秒后进行。第二次重试尝试在第一次重试尝试六秒后进行。而第三次重试尝试在第二次重试尝试 12 秒后进行。

MaxDelaySeconds(可选)

一个正整数,用于设置重试间隔可增加的最大值(以秒为单位)。该字段与 BackoffRate 字段配合使用会很有帮助。在此字段中指定的值将限制应用于每次连续重试尝试的回退率倍数所产生的指数等待时间。您必须为 MaxDelaySeconds 指定一个大于 0 小于 31622401 的值。

如果您未指定此值,则 Step Functions 不会限制两次重试尝试之间的等待时间。

JitterStrategy(可选)

一个字符串,用于确定是否在连续重试尝试之间的等待时间中包含抖动。抖动会将同时重试尝试的次数分散到一个随机的延迟时间间隔内,从而减少重试次数。此字符串可接受 FULLNONE 作为其值。默认值为 NONE

例如,您将 MaxAttempts 设置为 3,IntervalSeconds 设置为 2,BackoffRate 设置为 2。第一次重试尝试在错误发生两秒后进行。第二次重试在第一次重试尝试后四秒进行,第三次重试在第二次重试尝试后八秒进行。如果将 JitterStrategy 设置为 FULL,则第一次重试间隔在 0 至 2 秒之间随机进行,第二次重试间隔在 0 至 4 秒之间随机进行,第三次重试间隔在 0 至 8 秒之间随机进行。

重试字段示例

本节包含以下 Retry 字段示例。

提示

要在 Amazon Web Services 账户中部署错误处理工作流示例,请参阅《Amazon Step Functions 研讨会》中的错误处理模块。

示例 1 – 使用 BackoffRate 重试

下面的 Retry 示例进行了两次重试,第一次重试在等待 3 秒后进行。根据指定的 BackoffRate,Step Functions 会增加每次重试的间隔时间,直到达到重试尝试的最大次数。在下面的示例中,第二次重试在第一次重试 3 秒后开始。

"Retry": [ { "ErrorEquals": [ "States.Timeout" ], "IntervalSeconds": 3, "MaxAttempts": 2, "BackoffRate": 1 } ]
示例 2 – 使用 MaxDelaySeconds 重试

以下示例进行了三次重试尝试,并将 BackoffRate 产生的等待时间限制为 5 秒。第一次重试在等待 3 秒钟后进行。由于 MaxDelaySeconds 设置了最长等待时间限制,第二次和第三次重试将在前一次重试后等待 5 秒后进行。

"Retry": [ { "ErrorEquals": [ "States.Timeout" ], "IntervalSeconds": 3, "MaxAttempts": 3, "BackoffRate":2, "MaxDelaySeconds": 5, "JitterStrategy": "FULL" } ]

如果未设置 MaxDelaySeconds,第二次重试将在第一次重试后 6 秒进行,第三次重试将在第二次重试后 12 秒进行。

示例 3 – 重试除 States.Timeout 之外的所有错误

重试器的 ErrorEquals 字段中显示的保留名称 States.ALL 是一个通配符,与所有错误名称匹配。它必须单独显示在 ErrorEquals 数组中,并且必须显示在 Retry 数组的最后一个重试器中。名称 States.TaskFailed 也是一个通配符,可匹配除 States.Timeout 以外的任何错误。

下面的 Retry 字段示例重试了除 States.Timeout 以外的任何错误。

"Retry": [ { "ErrorEquals": [ "States.Timeout" ], "MaxAttempts": 0 }, { "ErrorEquals": [ "States.ALL" ] } ]
示例 4 – 复杂的重试场景

在单状态执行的上下文中,重试器的参数应用到对该重试器的所有访问。

考虑以下 Task 状态。

"X": { "Type": "Task", "Resource": "arn:aws:states:us-east-1:123456789012:task:X", "Next": "Y", "Retry": [ { "ErrorEquals": [ "ErrorA", "ErrorB" ], "IntervalSeconds": 1, "BackoffRate": 2.0, "MaxAttempts": 2 }, { "ErrorEquals": [ "ErrorC" ], "IntervalSeconds": 5 } ], "Catch": [ { "ErrorEquals": [ "States.ALL" ], "Next": "Z" } ] }

此任务连续失败四次,输出以下错误名称:ErrorAErrorBErrorCErrorB。出现以下结果:

  • 前两个错误与第一个重试器匹配,导致等待 1 秒和 2 秒。

  • 第三个错误与第二个重试器匹配,导致等待 5 秒。

  • 第四个错误也与第一个重试器匹配。但是,针对该特定错误的两次重试 (MaxAttempts) 已达到最大值。因此,该重试器失败,执行会通过 Catch 字段将工作流重定向到 Z 状态。

回退状态

TaskMapParallel 状态可以具有名为 Catch 的字段。此字段的值必须是称为捕获器 的对象的数组。

捕获器包含以下字段。

ErrorEquals(必填)

一个与错误名称匹配的非空字符串数组,由相同名称的重试器字段完全按其原样指定。

Next(必填)

一个字符串,必须与状态机的状态名称之一完全匹配。

ResultPath(可选)

一个路径,确定捕获器将什么输入发送到在 Next 字段中指定的状态。

当某个状态报告错误并且没有 Retry 字段或者重试无法解决错误时,Step Functions 按照数组中列出的顺序全面地扫描捕获器。当错误名称显示在捕获器的 ErrorEquals 字段中时,状态机转换为在 Next 字段中指定的状态。

捕获器的 ErrorEquals 字段中显示的保留名称 States.ALL 是一个通配符,与所有错误名称匹配。它必须单独显示在 ErrorEquals 数组中,并且必须显示在 Catch 数组的最后一个捕获器中。名称 States.TaskFailed 也是一个通配符,可匹配除 States.Timeout 以外的任何错误。

以下示例说明的是一个 Catch 字段,该字段在 Lambda 函数输出未处理的 Java 异常时转换为名为 RecoveryState 的状态。否则,该字段转换为 EndState 状态。

"Catch": [ { "ErrorEquals": [ "java.lang.Exception" ], "ResultPath": "$.error-info", "Next": "RecoveryState" }, { "ErrorEquals": [ "States.ALL" ], "Next": "EndState" } ]
注意

每个捕获器可以指定多个要处理的错误。

错误输出

Step Functions 转换为 Catch 名称中指定的状态时,对象通常包含字段 Cause。此字段的值是人类可读格式的错误说明。该对象称为错误输出

在本示例中,第一个捕获器包含一个 ResultPath 字段。其工作方式类似于状态顶级中的 ResultPath 字段,有两种可能的结果:

  • 它获取状态执行的结果,并覆盖状态的全部或部分输入。

  • 它获取结果并将其添加到输入中。在由捕获器处理错误时,状态执行的结果是错误输出。

因此,在本示例中,对于第一个捕获器,如果输入中还没有名为 error-info 的字段,捕获器就会将错误输出添加到输入中。然后,捕获器会将整个输入发送到 RecoveryState。对于第二个捕获器,错误输出会覆盖输入,捕获器只会将错误输出发送到 EndState

注意

如果您不指定 ResultPath 字段,则它会默认为 $,这会选择和覆盖整个输入。

当状态同时包含 RetryCatch 字段时,Step Functions 会首先使用任何合适的重试器。如果重试策略无法解决错误,Step Functions 会应用匹配的捕获器转换。

原因有效负载和服务集成

捕获器会返回一个字符串有效负载作为输出。在使用诸如 Amazon Athena 或 Amazon CodeBuild 之类的服务集成时,您可能需要将 Cause 字符串转换为 JSON。下面这个带有内置函数的 Pass 状态示例展示了如何将 Cause 字符串转换为 JSON。

"Handle escaped JSON with JSONtoString": { "Type": "Pass", "Parameters": { "Cause.$": "States.StringToJson($.Cause)" }, "Next": "Pass State with Pass Processing" },

使用 Retry 和使用 Catch 的状态机示例

以下示例中定义的状态机假定存在两个 Lambda 函数:一个始终失败,另一个等待足够长的时间,以允许发生状态机中定义的超时。

这是始终失败的 Lambda 函数的定义,返回消息 error。在后面的状态机示例中,此 Lambda 函数名为 FailFunction。有关创建 Lambda 函数的信息,请参阅第 1 步:创建 Lambda 函数部分。

exports.handler = (event, context, callback) => { callback("error"); };

这是一个休眠 10 秒的 Lambda 函数的定义。在后面的状态机示例中,此 Lambda 函数名为 sleep10

注意

当您在 Lambda 控制台中创建此 Lambda 函数时,请记住在 Advanced settings 部分中将 Timeout 值从 3 秒 (默认值) 更改为 11 秒。

exports.handler = (event, context, callback) => { setTimeout(function(){ }, 11000); };

使用 Retry 处理失败

此状态机使用 Retry 字段重试失败的函数,并输出错误名称 HandledError。该函数重试两次,在两次重试之间使用指数回退。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:FailFunction", "Retry": [ { "ErrorEquals": ["HandledError"], "IntervalSeconds": 1, "MaxAttempts": 2, "BackoffRate": 2.0 } ], "End": true } } }

此变体使用预定义的错误代码 States.TaskFailed,这与 Lambda 函数输出的任意错误匹配。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:FailFunction", "Retry": [ { "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 1, "MaxAttempts": 2, "BackoffRate": 2.0 } ], "End": true } } }
注意

作为最佳实操,引用 Lambda 函数的任务应处理 Lambda 服务异常。有关更多信息,请参阅处理 Lambda 服务异常

使用 Catch 处理失败

此示例使用 Catch 字段。当 Lambda 函数输出错误时,将会捕获错误,并且状态机转换为 fallback 状态。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:FailFunction", "Catch": [ { "ErrorEquals": ["HandledError"], "Next": "fallback" } ], "End": true }, "fallback": { "Type": "Pass", "Result": "Hello, Amazon Step Functions!", "End": true } } }

此变体使用预定义的错误代码 States.TaskFailed,这与 Lambda 函数输出的任意错误匹配。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:FailFunction", "Catch": [ { "ErrorEquals": ["States.TaskFailed"], "Next": "fallback" } ], "End": true }, "fallback": { "Type": "Pass", "Result": "Hello, Amazon Step Functions!", "End": true } } }

使用 Retry 处理超时

此状态机根据 TimeoutSeconds 中指定的超时值,使用 Retry 字段重试超时的 Task 状态。Step Functions 在此 Task 状态下重试两次 Lambda 函数调用,两次重试之间会出现指数回退。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:sleep10", "TimeoutSeconds": 2, "Retry": [ { "ErrorEquals": ["States.Timeout"], "IntervalSeconds": 1, "MaxAttempts": 2, "BackoffRate": 2.0 } ], "End": true } } }

使用 Catch 处理超时

此示例使用 Catch 字段。在出现超时的情况下,状态机会转换为 fallback 状态。

{ "Comment": "A Hello World example of the Amazon States Language using an Amazon Lambda function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:sleep10", "TimeoutSeconds": 2, "Catch": [ { "ErrorEquals": ["States.Timeout"], "Next": "fallback" } ], "End": true }, "fallback": { "Type": "Pass", "Result": "Hello, Amazon Step Functions!", "End": true } } }
注意

可以通过使用 ResultPath 来保留状态输入以及错误。请参阅 ResultPath 用于在 a 中同时包含错误和输入 Catch