使用变量在状态之间传递数据 - Amazon Step Functions
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

使用变量在状态之间传递数据

使用变量和 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 子工作流中的数据,并在状态机执行结束时返回。

Diagram showing step 1 assigning a value to $x, used in step 5.

支持变量的状态

以下状态类型支持 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 始终是指原始状态输入。

对于 TaskParallelMap 状态:

  • $states.result 指 API 或子工作流成功时的原始结果。

  • $states.errorOutput 指 API 或子工作流失败时的错误输出。

    $states.errorOutput 可用于 Catch 字段的 AssignOutput

如果在无法访问 $states.result$states.errorOutput 的字段和状态中尝试对其进行访问,则在创建、更新或验证状态机时将捕获该尝试。

$states.context 对象为您的工作流提供有关其具体执行的信息,例如 StartTime、任务令牌和初始工作流输入。要了解更多信息,请参阅在 Step Functions 中从上下文对象访问执行数据

变量名称语法

变量名称遵循 Unicode® 标准附录 #31 中所述的 Unicode 标识符规则。变量名称的第一个字符必须是 Unicode ID_Start 字符,第二个及后续字符必须是 Unicode ID_Continue 字符。变量名称的最大长度为 80 个字符。

变量名称约定类似于 JavaScript 和其他编程语言的规则。

变量作用域

Step Functions 工作流程通过使用 workflow-local 作用域来避免变量出现竞争条件。

workflow-local 作用域包括状态机状态字段内的所有状态,但不包括 Parallel 或 Map 状态内的状态。Parallel 或 Map 状态内的状态可以引用外部作用域变量,但它们创建和维护自己的独立 workflow-local 变量和值。

Parallel 分支和 Map 迭代可以访问外部作用域中的变量值,但它们无法访问其他并发分支或迭代中的变量值。处理错误时,Catch 中的 Assign 字段可以为外部作用域(即 Parallel/Map 状态存在的作用域)中的变量赋值。

异常:分布式 Map 状态当前不能引用外部作用域中的变量。

如果作用域中的任何状态为变量分配值,则该变量存在于作用域中。为避免常见错误,内部作用域中分配的变量不能与外部作用域中分配的变量同名。例如,如果顶级作用域为名为 myVariable 的变量赋值,则任何其他作用域(在 MapParallel 内)都不能同时为 myVariable 赋值。

对变量的访问取决于当前作用域。Parallel 和 Map 状态都有各自的作用域,但它们能够访问外部作用域中的变量。

当 Parallel 或 Map 状态完成时,它们的所有变量都将脱离作用域,不再可访问。使用 Output 字段将数据从 Parallel 分支和 Map 迭代中传出。

分配 ASL 中的字段

ASL 中的 Assign 字段用于为一个或多个变量赋值。Assign 字段位于每个状态(SucceedFail 除外)的顶级、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 对象。每个顶级字段都命名一个要赋值的变量。在前面的示例中,变量名称为 desiredPricemaximumWait。使用 JSONata 时,{% ... %} 表示可能包含变量或更复杂表达式的 JSONata 表达式。有关 JSONata 表达式的更多信息,请参阅 JSONata.org 文档

使用 JSONata 作为查询语言时,下图显示了如何并行处理 AssignOutput 字段。注意其含义:为变量赋值并不会影响状态输出

Diagram showing a comparison of JSONPath and JSONata flow.

以下 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)。以下步骤描述了此过程:

  1. 使用所有变量的当前值计算所有表达式。

    表达式 "{% $a %}" 的计算结果将为 6,"{% $x %}" 的计算结果为 3。

  2. 接下来,进行赋值:

    将为 $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 同时发生:

Logical diagram of a state that uses JSONPath query language.

在 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,MapParallel 状态下,$ 指的是 API/子工作流结果。在 ChoiceWait 状态下,$ 指的是有效输入,即 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" }