使用变量在状态之间传递数据
使用变量和 JSONata 管理状态
Step Functions 最近添加了变量和 JSONata 来管理状态和转换数据。
要了解更多信息,请阅读博客文章使用 Amazon Step Functions 中的变量和 JSONata 简化开发人员体验
使用变量和状态输出,您可以在工作流的各个步骤之间传递数据。
使用工作流变量,您可以在一个步骤中存储数据,并在将来的步骤中检索该数据。例如,您可以存储包含日后可能需要的数据的 API 响应。相反,状态输出只能用作紧接着下一步的输入。
变量的概念性概述
使用工作流变量,您可以存储数据以备日后参考。例如,步骤 1 可能会存储 API 请求的结果,以便之后在步骤 5 中重复使用该请求的一部分。
在以下场景中,状态机从 API 获取数据一次。在步骤 1 中,工作流将返回的 API 数据(每个状态最多 256 KiB)存储在变量“x”中,以便在后续步骤中使用。
如果不使用变量,则需要通过输出将该数据从步骤 1 依次传递到步骤 2、步骤 3、步骤 4,然后在步骤 5 中使用。如果这些中间步骤不需要该数据,怎么办? 通过输出和输入在状态之间传递数据将不必要。
借助变量,您可以存储数据并在将来的任何步骤中使用这些数据。您还可以在不中断数据流的情况下修改、重新排列或添加步骤。考虑到变量的灵活性,您可能只需使用 Output 来返回 Parallel 和 Map 子工作流中的数据,并在状态机执行结束时返回。
支持变量的状态
以下状态类型支持 Assign 以声明变量并为其赋值:Pass、Task、Map、Parallel、Choice、Wait。
要设置变量,请提供一个包含变量名称和值的 JSON 对象:
"Assign": {
"productName": "product1",
"count" : 42,
"available" : true
}
要引用变量,请在名称前面加上美元符号($),例如 $productName。
保留变量:$states
Step Functions 定义了一个名为 $states 的保留变量。在 JSONata 状态下,将以下结构分配给 $states 以在 JSONata 表达式中使用:
# Reserved $states variable in JSONata states
$states = {
"input": // Original input to the state
"result": // API or sub-workflow's result (if successful)
"errorOutput": // Error Output (only available in a Catch)
"context": // Context object
}
进入状态时,Step Functions 会将状态输入分配给 $states.input。$states.input 的值可用于所有接受 JSONata 表达式的字段。$states.input 始终是指原始状态输入。
对于 Task、Parallel 和 Map 状态:
-
$states.result指 API 或子工作流成功时的原始结果。 -
$states.errorOutput指 API 或子工作流失败时的错误输出。$states.errorOutput可用于Catch字段的Assign或Output。
如果在无法访问 $states.result 或 $states.errorOutput 的字段和状态中尝试对其进行访问,则在创建、更新或验证状态机时将捕获该尝试。
$states.context 对象为您的工作流提供有关其具体执行的信息,例如 StartTime、任务令牌和初始工作流输入。要了解更多信息,请参阅在 Step Functions 中从上下文对象访问执行数据 。
变量名称语法
变量名称遵循 Unicode® 标准附录 #31
变量名称约定类似于 JavaScript 和其他编程语言的规则。
变量作用域
Step Functions 工作流程通过使用 workflow-local 作用域来避免变量出现竞争条件。
workflow-local 作用域包括状态机状态字段内的所有状态,但不包括 Parallel 或 Map 状态内的状态。Parallel 或 Map 状态内的状态可以引用外部作用域变量,但它们创建和维护自己的独立 workflow-local 变量和值。
Parallel 分支和 Map 迭代可以访问外部作用域中的变量值,但它们无法访问其他并发分支或迭代中的变量值。处理错误时,Catch 中的 Assign 字段可以为外部作用域(即 Parallel/Map 状态存在的作用域)中的变量赋值。
异常:分布式 Map 状态当前不能引用外部作用域中的变量。
如果作用域中的任何状态为变量分配值,则该变量存在于作用域中。为避免常见错误,内部作用域中分配的变量不能与外部作用域中分配的变量同名。例如,如果顶级作用域为名为 myVariable 的变量赋值,则任何其他作用域(在 Map、Parallel 内)都不能同时为 myVariable 赋值。
对变量的访问取决于当前作用域。Parallel 和 Map 状态都有各自的作用域,但它们能够访问外部作用域中的变量。
当 Parallel 或 Map 状态完成时,它们的所有变量都将脱离作用域,不再可访问。使用 Output 字段将数据从 Parallel 分支和 Map 迭代中传出。
分配 ASL 中的字段
ASL 中的 Assign 字段用于为一个或多个变量赋值。Assign 字段位于每个状态(Succeed 和 Fail 除外)的顶级、Choice 状态规则内部和 Catch 字段内部。例如:
# Example of Assign with JSONata
"Store inputs": {
"Type": "Pass",
"Next": "Get Current Price",
"Comment": "Store the input desired price into a variable: $desiredPrice",
"Assign": {
"desiredPrice": "{% $states.input.desired_price %}",
"maximumWait": "{% $states.input.max_days %}"
}
},
Assign 字段采用一个 JSON 对象。每个顶级字段都命名一个要赋值的变量。在前面的示例中,变量名称为 desiredPrice 和 maximumWait。使用 JSONata 时,{% ... %} 表示可能包含变量或更复杂表达式的 JSONata 表达式。有关 JSONata 表达式的更多信息,请参阅 JSONata.org 文档
使用 JSONata 作为查询语言时,下图显示了如何并行处理 Assign 和 Output 字段。注意其含义:为变量赋值并不会影响状态输出。
以下 JSONata 示例从状态输入中检索 order.product。变量 currentPrice 设置为任务结果中的一个值。
# Example of Task with JSONata assignment from result
{
"Type": "Task",
...
"Assign": {
"product": "{% $states.input.order.product %}",
"currentPrice": "{% $states.result.Payload.current_price %}"
},
"Next": "the next state"
}
注意:不能为变量的一部分赋值。例如,可以 "Assign":{"x":42},但不能 "Assign":{"x.y":42} 或 "Assign":{"x[2]":42}。
分配字段中的求值顺序
Step Functions 状态中的所有变量引用都使用状态输入时的值。
前述事实对于了解 Assign 字段如何为一个或多个变量赋值非常重要。首先,计算新值,然后 Step Functions 将新值分配给变量。新的变量值将在下一个状态开始时生效。例如,考虑以下 Assign 字段:
# Starting values: $x=3, $a=6
"Assign": {
"x": "{% $a %}",
"nextX": "{% $x %}"
}
# Ending values: $x=6, $nextX=3
在前述示例中,变量 x 既被赋值,又被引用。
请记住,所有表达式都是先求值,再进行赋值。新分配的值将在下一个状态下生效。
我们来详细看一下这个例子。假设在之前的状态下,为 $x 分配的值为三(3),为 $a 分配的值为六(6)。以下步骤描述了此过程:
-
使用所有变量的当前值计算所有表达式。
表达式
"{% $a %}"的计算结果将为 6,"{% $x %}"的计算结果为 3。 -
接下来,进行赋值:
将为
$x分配六(6)的值将为
$nextX分配三(3)的值
注意:如果 $x 之前未分配过,则该示例将失败,因为 $x 将是未定义。
总而言之,Step Functions 会先计算所有表达式,再进行赋值。变量在 Assign 字段中出现的顺序并不重要。
限制
对于标准和快速工作流,单个变量的最大大小为 256Kib。
单个 Assign 字段中所有变量的最大组合大小也为 256Kib。例如,您可以将 X 和 Y 分配为 128KiB 的大小,但不能在同一 Assign 字段中将 X 和 Y 都分配为 256KiB 的大小。
每次执行时,所有已存储变量的总大小不能超过 10MiB。
在 JSONPath 状态下使用变量
在使用 JSONPath 作为查询语言的状态下,变量也可用。
您可以在任何接受 JSONPath 表达式($. 或 $$. 语法)的字段中引用变量,但 ResultPath 字段除外,该字段在状态输入中指定用于注入状态结果的位置。不能在 ResultPath 中使用变量。
在 JSONPath 中,$ 符号指的是“当前”值,$$ 表示状态上下文对象。JSONPath 表达式可以像在 $.customer.name 中那样以 $. 开头。您可以像在 $$.Execution.Id 中那样使用 $$. 访问上下文。
要引用变量,也可以在变量名前使用 $ 符号,例如 $x 或 $order.numItems。
例如,在接受内置函数的 JSONPath 字段中,可以在参数中使用变量,例如 States.Format('The order number is {}', $order.number)。
下图说明了 JSONPath 任务中的分配步骤是如何与 ResultSelector 同时发生:
在 JSONPath 中分配变量
JSONPath 变量分配的行为与有效载荷模板类似。以 .$ 结尾的字段表示该值是一个 JSONPath 表达式,Step Functions 在状态机执行期间将其计算为一个值(例如:$.order..product 和 $.order.total)。
# Example of Assign with JSONPath
{
"Type": "Task",
...
"Assign": {
"products.$": "$.order..product",
"orderTotal.$": "$.order.total"
},
"Next": "the next state"
}
对于 JSONPath 状态,Assign 字段中 $ 的值取决于状态类型。在 Task,、Map、Parallel 状态下,$ 指的是 API/子工作流结果。在 Choice 和 Wait 状态下,$ 指的是有效输入,即 InputPath 应用于状态输入之后的值。对于 Pass,$ 指的是结果,即是由 Result 字段生成,还是由 InputPath/Parameters 字段生成。
下面的 JSONPath 示例将 JSON 对象分配给 details 变量,将 JSONPath 表达式 $.result.code 的结果分配给变量 resultCode,将 JSONPath 表达式 States.Format('Hello {}', $customer.name) 的结果分配给 message。如果处于 Task 状态,则 $.order.items 和 $.result.code 中的 $ 指的是 API 结果。为 startTime 变量分配来自上下文对象 $$.Execution.StartTime 的值。
"Assign": {
"details": {
"status": "SUCCESS",
"lineItems.$": "$.order.items"
},
"resultCode.$": "$.result.code",
"message.$": "States.Format('Hello {}', $customer.name)",
"startTime.$": "$$.Execution.StartTime"
}