如果您喜欢流畅的编码,那么AWS 云开发工具包步骤函数定义就在您的街道上。然而,事情并不像你想象的那么简单。

在我之前的文章使用适用于 VS 代码的 AWS 工具包的更简单的步骤函数中,我赞扬了将 AWS 工具包与AWS 无服务器应用程序模型 (SAM)结合使用的优点。现在,我将提出一种完全不同的做事方式。

这篇文章假设您对 CDK 有一定的了解。如果您不是,那么AWS CDK 入门指南是最好的起点。指南中的以下引用很好地概述了 CDK 背后的关键概念。

"AWS CDK 应用程序是用 TypeScript、JavaScript、Python、Java 或 C# 编写的应用程序,它使用 AWS CDK 定义 AWS 基础设施。应用程序定义一个或多个堆栈。堆栈(相当于 AWS CloudFormation 堆栈)包含构造,每个都定义了一个或多个具体的 AWS 资源,例如 Amazon S3 存储桶、Lambda 函数、Amazon DynamoDB 表等。”

对于这篇文章,我复制了原始的基于 SAM 的repo,然后将其修改为使用 CDK。结果可以在这里找到。

CDK 中的步骤函数需要引用它们调用的函数。在演示项目中,所有函数都在同一个文件中并遵循命名约定。这使我能够在Stack类中创建以下方法:

  private addFunction(functionName: string): lambda.Function {
    return new lambdaNodejs.NodejsFunction(this, `${functionName}Function`, {
      entry: path.join(__dirname, '..', 'src', 'functions', 'index.ts'),
      handler: `handle${functionName}`,
    });
  }

有了这个,我就可以声明所有必需的函数,如下所示:

    const performIdentityCheckFunction = this.addFunction('PerformIdentityCheck');
    const aggregateIdentityResultsFunction = this.addFunction('AggregateIdentityResults');
    const performAffordabilityCheckFunction = this.addFunction('PerformAffordabilityCheck');
    const sendEmailFunction = this.addFunction('SendEmail');
    const notifyUnderwriterFunction = this.addFunction('NotifyUnderwriter');

我现在可以将注意力转向转换阶跃函数本身。 step 函数是处理贷款申请的简化流程。第一步对每个申请人进行身份检查,然后汇总结果

对于 SAM,这是使用以下 YAML 定义的:

  PerformIdentityChecks:
    Type: Map
    InputPath: "$.application"
    ItemsPath: "$.applicants"
    ResultPath: "$.identityResults"
    Iterator:
      StartAt: PerformIdentityCheck
      States:
        PerformIdentityCheck:
          Type: Task
          Resource: "${PerformIdentityCheckFunctionArn}"
          End: true
    Next: AggregateIdentityResults

  AggregateIdentityResults:
    Type: Task
    Resource: "${AggregateIdentityResultsFunctionArn}"
    InputPath: "$.identityResults"
    ResultPath: "$.overallIdentityResult"
    Next: EvaluateIdentityResults

CDK 使用流利的语法,其属性与上述匹配。我想很简单,只需在 TypeScript 中复制相同的逻辑即可:

const processApplicationStateMachine = new sfn.StateMachine(
  this,
  'ProcessApplicationStateMachine',
  {
    definition: sfn.Chain.start(
      new sfn.Map(this, 'PerformIdentityChecks', {
        inputPath: '$.application',
        itemsPath: '$.applicants',
        resultPath: '$.identityResults',
      })
        .iterator(
          new sfnTasks.LambdaInvoke(this, 'PerformIdentityCheck', {
            lambdaFunction: performIdentityCheckFunction,
          })
        )
        .next(
          new sfnTasks.LambdaInvoke(this, 'AggregateIdentityResults', {
            lambdaFunction: aggregateIdentityResultsFunction,
            inputPath: '$.identityResults',
            resultPath: '$.overallIdentityResult',
          })
        )
    ),
  }
);

下一步是让我测试它。使用 AWS Toolkit,我右键单击 step 函数并使用项目中的 JSON 测试文件之一。

aws-toolkit-start-execution.png

然后我进入 AWS 控制台,很高兴看到它全是绿色的。

surface-success.png

然而... 查看 PerformIdentityCheck 的步骤输出,我看到了以下输出:

{
  "ExecutedVersion": "$LATEST",
  "Payload": {
    "success": false
  },
  "SdkHttpMetadata": {
      <snip>
  },
  "SdkResponseMetadata": {
    "RequestId": "76e41976-672d-4be0-a4d2-a5b80e7f9afe"
  },
  "StatusCode": 200
}

这不是我所期望的,但是通过使用函数上的outputPath属性来选择Payload可以很容易地解决这个问题。例如。:

new sfnTasks.LambdaInvoke(this, 'PerformIdentityCheck', {
  lambdaFunction: performIdentityCheckFunction,
  outputPath: '$.Payload',
})

有了这个改变,我再次部署,并启动了我的测试。结果......彻底失败

unexpected-failure.png

我检查了 PerformIdentityCheck 步骤的输出,一切都符合预期,我检查了 map 步骤的输出,一切都符合预期。

问题出在 AggregateIdentityResults 上。它已按预期执行,输出以下内容。

{
  "resourceType": "lambda",
  "resource": "invoke",
  "output": {
    "ExecutedVersion": "$LATEST",
    "Payload": false,
    <snip>
    "StatusCode": 200
  },
  "outputDetails": {
    "truncated": false
  }
}

但是,执行后会抛出Invalid path '$.Payload' : No results for path: $['Payload']错误。

{
  "error": "States.Runtime",
  "cause": "An error occurred while executing the state 'AggregateIdentityResults' (entered at the event id #13). Invalid path '$.Payload' : No results for path: $['Payload']"
}

提示浪费了一个小时试图弄清楚为什么$.Payload为一个功能任务工作,而不是另一个。我最终确实找到了这一点(见帖子末尾),但我的调查导致我从 2020 年 4 月开始出现以下问题:RunLambdaTask with outputPath not working

这为我指明了解决方案的方向。这是使用文档定义的payloadResponseOnly属性,如下所示:

'以仅返回有效负载响应而没有其他元数据的方式调用 Lambda。

例如。:

new sfnTasks.LambdaInvoke(this, 'PerformIdentityCheck', {
  lambdaFunction: performIdentityCheckFunction,
  payloadResponseOnly: true,
})

有了这个,我重新运行了测试,并在 AWS 中检查了结果。

{
  "application": {
    <snip>
  },
  "identityResults": [
    {
      "success": false
    }
  ],
  "overallIdentityResult": false
}

**万岁!**这完全符合预期,函数调用没有返回无关数据。

通过查看生成的定义,我确实注意到,用于调用函数的生成 ASL 取决于payloadResponseOnly的值。没有payloadResponseOnly: true的定义如下生成:

    "AggregateIdentityResults": {
      <snap>
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "arn:aws:lambda:eu-west-2:361728023653:function:ProcessApplicationStack-AggregateIdentityResultsFu-B7MG7QWC1VLN",
        "Payload.$": "$"
      }
    }

在使用payloadResponseOnly: true时,我们得到以下与基于 SAM 的原始定义相匹配的内容:

    "AggregateIdentityResults": {
      <snip>
      "Resource": "arn:aws:lambda:eu-west-2:361728023653:function:ProcessApplicationStack-AggregateIdentityResultsFu-B7MG7QWC1VLN"
    }

这种差异一定是响应差异的原因,但我不知道为什么会这样。但是,我现在可以得到我想要的结果,所以我将继续前进。

这部分结束了,我预计会更进一步,但这个结果对于软件开发来说是相当标准的。在第 2 部分中,我将继续将 step 函数转换为 CDK,并记录我在此过程中遇到的挑战。

编辑:Payload错误的原因是由于我对路径的处理方式有误解。我缺少的关键位如下:

“OutputPath 是在应用 ResultPath 后计算的。所有服务集成都返回元数据作为其响应的一部分。使用 ResultPath 时,无法将任务输出的子集合并到输入。”

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐