使用 AWS Step Functions 实现无服务器传奇模式 - AWS Prescriptive Guidance

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

使用 AWS Step Functions 实现无服务器传奇模式

由 Tabby Ward (AWS)、Rohan Mehta () 和 Rimpy Tewani (AWS) 创作 AWS

环境:PoC 或试点

技术:现代化;无服务器

工作负载:开源

AWS服务:亚马逊 API Gateway;亚马逊 DynamoDB;Lambda;亚马逊AWS;Step Functions SNS AWS

Summary

在微服务架构中,主要目标是构建解耦且独立的组件,以提高应用程序的敏捷性、灵活性和更快的上市时间。解耦后,每个微服务组件都配有自己的数据持久层。在分布式架构中,业务事务可跨越多个微服务。由于这些微服务不能使用单个原子性、一致性、隔离、耐久性 (ACID) 事务,因此最终可能会出现部分事务。在这种情况下,需要一些控制逻辑撤销已经处理的事务。分布式 saga 模式通常用于该目的。 

Saga 模式是一种故障管理模式,有助于在分布式应用程序中建立一致性,并协调多个微服务之间的事务以保持数据一致性。当您使用 saga 模式时,每个执行事务的服务都会发布事件,该事件会触发后续服务执行链中的下一个事务。这种情况一直持续至链中的最后一笔事务完成。如果业务事务处理失败,saga 会编排一系列补偿性事务,以撤销先前事务所做的更改。

此模式演示了如何使用 AWS Step Functions、Lambda 和 Amazon Dynamo AWS DB 等无服务器技术自动设置和部署示例应用程序(用于处理差旅预订)。该示例应用程序还使用 Amazon API Gateway 和亚马逊简单通知服务 (AmazonSNS) 来实现传奇执行协调器。该模式可以与基础设施即代码 (IaC) 框架一起部署,例如 AWS Cloud Development Kit (AWSCDK)、AWS无服务器应用程序模型 (AWSSAM) 或 Terraform。

有关传奇模式和其他数据持久性模式的更多信息,请参阅AWS规范性指南网站上的指南 “在微服务中启用数据持久性”。

先决条件和限制

先决条件

  • 一个活动的 AWS 账户。

  • 创建AWS CloudFormation 堆栈的权限。有关更多信息,请参阅 CloudFormation 文档中的控制访问权限

  • 使用您的AWS账户配置您选择的 IaC 框架(AWSCDKAWSSAM、或 Terraform),以便您可以使用该框架CLI来部署应用程序。

  • NodeJS,用于在本地构建和运行应用程序。

  • 您选择的代码编辑器(例如 Visual Studio Code、Sublime 或 Atom)。

产品版本

限制

事件源是在微服务架构中实现 saga 编排模式的一种自然方式,在这种架构中,所有组件都是松耦合的,彼此之间没有直接的了解。如果您的事务涉及少量步骤(三到五步),那么 saga 模式可能非常合适。但是,复杂性会随微服务数量和步骤数量的增加而增加。 

使用此类设计时,测试和调试可能会变得困难,因为必须运行所有服务才能模拟事务模式。

架构

目标架构

拟议的架构使用 AWS Step Functions来构建传奇模式,用于预订航班、预订汽车租赁和处理度假付款。

以下工作流图介绍了旅行预订系统的典型流程。工作流程包括预订航空旅行 (“ReserveFlight”)、预订汽车 (“ReserveCarRental”)、处理付款 (“ProcessPayment”)、确认航班预订 (“ConfirmFlight”) 和确认租车 (“ConfirmCarRental”),然后在这些步骤完成后发出成功通知。但是,如果系统在运行其中任何一个事务时遇到任何错误,它会开始向后失败。例如,付款处理错误 (“ProcessPayment”) 会触发退款 (“RefundPayment”),然后退款会触发取消租车和航班(“CancelRentalReservation” 和 “CancelFlightReservation”),从而以失败消息结束整个交易。

这种模式为图表中突出显示的每项任务部署了单独的 Lambda 函数,还为航班、汽车租赁和付款部署了三项 DynamoDB 表。每个 Lambda 函数都创建、更新或删除相应的 DynamoDB 表中的行,具体取决于事务是确认还是回滚。该模式使用 Amazon SNS 向订阅者发送短信 (SMS) 消息,通知他们交易失败或成功。 

基于传奇模式的旅行预订系统的工作流程。

自动化和扩缩

您可以使用其中一个 IaC 框架为此架构创建配置。通过以下链接之一获取首选 IaC。

工具

AWS服务

  • AWSSt ep Functions 是一项无服务器编排服务,可让您结合 Lam AWS bda 函数和其他AWS服务来构建业务关键型应用程序。通过 Step Functions 图形控制台,您可以将应用程序的工作流视为一系列事件驱动的步骤。

  • Amazon DynamoDB 是一项完全托管的SQL无数据库服务,可提供快速、可预测的性能和无缝扩展。您可以使用 DynamoDB 创建一个数据库表来存储和检索任意量级的数据,并支持任何级别的请求流量。

  • AWSLambda 是一项计算服务,允许您在不预配置或管理服务器的情况下运行代码。只有在需要时 Lambda 才运行您的代码,并且能自动扩缩,从每天几个请求扩展到每秒数千个请求。

  • Amazon API Gateway 是一项用于创建、发布、维护RESTHTTP、监控和保护的AWS服务,而且 WebSocket APIs规模不限。

  • 亚马逊简单通知服务 (AmazonSNS) 是一项托管服务,可从发布者向订阅者传送消息。

  • AWSCloud Development Kit (AWSCDK) 是一个软件开发框架,用于使用熟悉的编程语言(例如 Python TypeScript JavaScript、Java 和 C#/Net)来定义云应用程序资源。

  • AWS无服务器应用程序模型 (AWSSAM) 是一个用于构建无服务器应用程序的开源框架。它提供了用于表达函数APIs、数据库和事件源映射的速记语法。

代码

可以在以下链接中找到演示传奇模式的示例应用程序的代码,包括 IaC 模板(AWSCDKAWSSAM、或 Terraform)、Lambda 函数和 DynamoDB 表。按照首个操作说明中的说明安装这些工具。

操作说明

任务描述所需技能

安装NPM软件包。

创建一个新目录,在终端中导航到该目录,然后在此模式前面的 “代码” 部分中克隆您选择的 GitHub 存储库。

在包含该package.json文件的根文件夹中,运行以下命令下载并安装所有 Node Package Manager (NPM) 软件包:

npm install
开发人员、云架构师

编译脚本。

在根文件夹中,运行以下命令以指示 TypeScript 转译器创建所有必需 JavaScript 的文件:

npm run build
开发人员、云架构师

注意更改和重新编译。

在根文件夹,在单独的终端窗口中运行以下命令,以监视代码更改,并在检测到更改时编译代码:

npm run watch
开发人员、云架构师

(AWSCDK仅限)运行单元测试。

如果你使用的是 AWSCDK,请在根文件夹中运行以下命令来执行 Jest 单元测试:

npm run test
开发人员、云架构师
任务描述所需技能

将演示堆栈部署到AWS。

重要:该应用程序不受AWS区域限制。如果您使用配置文件,则必须在AWS命令行界面 (AWSCLI) 配置文件中或通过AWSCLI环境变量显式声明区域。

在根文件夹中,运行以下命令以创建部署程序集并将其部署到默认AWS帐户和区域。

AWS CDK:

cdk bootstrap cdk deploy

AWS SAM:

sam build sam deploy --guided

Terraform:

terraform init terraform apply

此步骤可能需要几分钟时间才能完成。此命令使用为配置的默认凭据AWSCLI。

记下部署完成后控制台上显示的API网关URL。您将需要这些信息测试 saga 的执行流程。

开发人员、云架构师

将已部署的堆栈与当前状态比较。

在根文件夹中,运行以下命令,将已部署的堆栈与更改源代码后的当前状态比较:

AWS CDK:

cdk diff

AWS SAM:

sam deploy

Terraform:

terraform plan
开发人员、云架构师
任务描述所需技能

测试 saga 执行流程。

导航到您在部署堆栈时在前面的步骤中记下的API网关URL。这URL会触发状态机启动。有关如何通过传递不同的URL参数来操纵状态机流程的更多信息,请参阅 “其他信息” 部分。

要查看结果,请登录AWS管理控制台并导航到 Step Functions 控制台。在这里,您可以看到 saga 状态机的每一个步骤。您还可以查看 DynamoDB 表以查看已插入、更新或删除记录。如果您经常刷新屏幕,则可以看到事务状态从 pending 变为 confirmed。 

您可以通过使用手机号码更新stateMachine.ts文件中的代码来订阅该SNS主题,以便在预订成功或失败时接收SMS消息。有关更多信息,请参阅 Amazon SNS 的 “其他信息” 部分。

开发人员、云架构师
任务描述所需技能

清理资源。

若要清理为此应用程序部署的资源,可以使用以下命令之一。

AWS CDK:

cdk destroy

AWS SAM:

sam delete

Terraform:

terraform destroy
应用程序开发人员、云架构师

相关资源

技术论文

AWS服务文档

教程

其他信息

代码

出于测试目的,此模式部署API网关和触发 Step Functions 状态机的测试 Lambda 函数。使用 Step Functions,您可以通过传递run_type参数来模仿 “、”、“” ReserveFlight、“” 和 “ReserveCarRental” 中的故障ProcessPayment,从而控制旅行预订系统的功能ConfirmCarRental。ConfirmFlight

sagaLambda 函数 (sagaLambda.ts) 从API网关中的查询参数中获取输入URL,创建以下JSON对象,然后将其传递给 Step Functions 执行:

let input = { "trip_id": tripID, // value taken from query parameter, default is AWS request ID "depart_city": "Detroit", "depart_time": "2021-07-07T06:00:00.000Z", "arrive_city": "Frankfurt", "arrive_time": "2021-07-09T08:00:00.000Z", "rental": "BMW", "rental_from": "2021-07-09T00:00:00.000Z", "rental_to": "2021-07-17T00:00:00.000Z", "run_type": runType // value taken from query parameter, default is "success" };

您可以通过传递以下URL参数来试验 Step Functions 状态机的不同流程:

  • 成功执行 ─ https://{api gateway url}

  • 预订航班失败 ─ https://{api gateway url}? runType= failFlightsReservation

  • 确认飞行失败 ─ https://{api gateway url}? runType= failFlightsConfirmation

  • 预订租车失败 ─ https://{api gateway url}? runType= failCarRental 预订

  • 确认租车失败 ─ https://{api gateway url}? runType= failCarRental 确认

  • 处理付款失败 ─ https://{api 网关网址}? runType= failPayment

  • 传递行程 ID ─ https://{api gateway url}? tripid= {默认情况下,行程 ID 将是请求编号} AWS

IaC 模板

链接的存储库包含 IaC 模板,您可使用这些模板来创建整个示例旅行预订应用程序。

DynamoDB 表

以下是关于航班、租车和付款表的数据模型。

Flight Data Model: var params = { TableName: process.env.TABLE_NAME, Item: { 'pk' : {S: event.trip_id}, 'sk' : {S: flightReservationID}, 'trip_id' : {S: event.trip_id}, 'id': {S: flightReservationID}, 'depart_city' : {S: event.depart_city}, 'depart_time': {S: event.depart_time}, 'arrive_city': {S: event.arrive_city}, 'arrive_time': {S: event.arrive_time}, 'transaction_status': {S: 'pending'} } }; Car Rental Data Model: var params = { TableName: process.env.TABLE_NAME, Item: { 'pk' : {S: event.trip_id}, 'sk' : {S: carRentalReservationID}, 'trip_id' : {S: event.trip_id}, 'id': {S: carRentalReservationID}, 'rental': {S: event.rental}, 'rental_from': {S: event.rental_from}, 'rental_to': {S: event.rental_to}, 'transaction_status': {S: 'pending'} } }; Payment Data Model: var params = { TableName: process.env.TABLE_NAME, Item: { 'pk' : {S: event.trip_id}, 'sk' : {S: paymentID}, 'trip_id' : {S: event.trip_id}, 'id': {S: paymentID}, 'amount': {S: "750.00"}, // hard coded for simplicity as implementing any monetary transaction functionality is beyond the scope of this pattern 'currency': {S: "USD"}, 'transaction_status': {S: "confirmed"} } };

Lambda 函数

将创建以下函数,以支持 Step Functions 中的状态机流程和执行:

  • 预订航班:在 DynamoDB 航班表中插入一条带有 pendingtransaction_status 的记录以预订航班。

  • 确认航班:更新 DynamoDB 航班表中的记录,将 transaction_status 设置为 confirmed,以确认航班。

  • 取消航班预订:从 DynamoDB 航班表内删除记录,以取消待处理的航班。

  • 预订租车:在 Dynamo CarRentals DB 表中插入一条带有 transaction_status a pending 的记录以预订租车。

  • 确认租车:更新 Dynamo CarRentals DB 表中的记录,将其transaction_status设置为,confirmed以确认租车。

  • 取消租车预订:从 Dynamo CarRentals DB 表中删除记录,以取消待处理的租车。

  • 处理付款:在 DynamoDB 付款表中插入付款记录。

  • 取消付款:从 DynamoDB 付款表删除付款记录。

Amazon SNS

该示例应用程序创建了以下主题和订阅,用于发送SMS消息并通知客户预订成功或失败。如果您想在测试示例应用程序时接收短信,请在状态机定义文件中使用您的有效电话号码更新SMS订阅。

AWSCDK片段(在以下代码的第二行中添加电话号码):

const topic = new sns.Topic(this, 'Topic'); topic.addSubscription(new subscriptions.SmsSubscription('+11111111111')); const snsNotificationFailure = new tasks.SnsPublish(this ,'SendingSMSFailure', { topic:topic, integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE, message: sfn.TaskInput.fromText('Your Travel Reservation Failed'), }); const snsNotificationSuccess = new tasks.SnsPublish(this ,'SendingSMSSuccess', { topic:topic, integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE, message: sfn.TaskInput.fromText('Your Travel Reservation is Successful'), });

AWSSAM片段(用您的有效电话号码替换+1111111111字符串):

StateMachineTopic11111111111: Type: 'AWS::SNS::Subscription' Properties: Protocol: sms TopicArn: Ref: StateMachineTopic Endpoint: '+11111111111' Metadata: 'aws:sam:path': SamServerlessSagaStack/StateMachine/Topic/+11111111111/Resource

Terraform 片段(用您的有效电话号码替换 +111111111 字符串):

resource "aws_sns_topic_subscription" "sms-target" { topic_arn = aws_sns_topic.topic.arn protocol = "sms" endpoint = "+11111111111" }

成功预订

以下流程说明了成功的预订ReserveFlight,“” 和 “ReserveCarRentalProcessPayment” 后面是 “ConfirmFlight” 和 “” ConfirmCarRental。通过向该SNS主题的订阅者发送SMS消息,向客户通知预订成功。

Step Functions 使用传奇模式成功实现预留的示例。

预订失败

此流程是 saga 模式失败的一个例子。如果在预订航班和租车后,“ProcessPayment” 失败,则按相反的顺序取消步骤。 取消预订,并通过向该SNS主题的订阅者发送SMS消息将失败通知客户。

Step Functions 使用传奇模式实现的预留失败示例。