本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用 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 存储库。 在包含该
| 开发人员、云架构师 |
编译脚本。 | 在根文件夹中,运行以下命令以指示 TypeScript 转译器创建所有必需 JavaScript 的文件:
| 开发人员、云架构师 |
注意更改和重新编译。 | 在根文件夹,在单独的终端窗口中运行以下命令,以监视代码更改,并在检测到更改时编译代码:
| 开发人员、云架构师 |
(AWSCDK仅限)运行单元测试。 | 如果你使用的是 AWSCDK,请在根文件夹中运行以下命令来执行 Jest 单元测试:
| 开发人员、云架构师 |
任务 | 描述 | 所需技能 |
---|---|---|
将演示堆栈部署到AWS。 | 重要:该应用程序不受AWS区域限制。如果您使用配置文件,则必须在AWS命令行界面 (AWSCLI) 配置文件中或通过AWSCLI环境变量显式声明区域。 在根文件夹中,运行以下命令以创建部署程序集并将其部署到默认AWS帐户和区域。 AWS CDK:
AWS SAM:
Terraform:
此步骤可能需要几分钟时间才能完成。此命令使用为配置的默认凭据AWSCLI。 记下部署完成后控制台上显示的API网关URL。您将需要这些信息测试 saga 的执行流程。 | 开发人员、云架构师 |
将已部署的堆栈与当前状态比较。 | 在根文件夹中,运行以下命令,将已部署的堆栈与更改源代码后的当前状态比较: AWS CDK:
AWS SAM:
Terraform:
| 开发人员、云架构师 |
任务 | 描述 | 所需技能 |
---|---|---|
测试 saga 执行流程。 | 导航到您在部署堆栈时在前面的步骤中记下的API网关URL。这URL会触发状态机启动。有关如何通过传递不同的URL参数来操纵状态机流程的更多信息,请参阅 “其他信息” 部分。 要查看结果,请登录AWS管理控制台并导航到 Step Functions 控制台。在这里,您可以看到 saga 状态机的每一个步骤。您还可以查看 DynamoDB 表以查看已插入、更新或删除记录。如果您经常刷新屏幕,则可以看到事务状态从 您可以通过使用手机号码更新 | 开发人员、云架构师 |
任务 | 描述 | 所需技能 |
---|---|---|
清理资源。 | 若要清理为此应用程序部署的资源,可以使用以下命令之一。 AWS CDK:
AWS SAM:
Terraform:
| 应用程序开发人员、云架构师 |
相关资源
技术论文
AWS服务文档
教程
其他信息
代码
出于测试目的,此模式部署API网关和触发 Step Functions 状态机的测试 Lambda 函数。使用 Step Functions,您可以通过传递run_type
参数来模仿 “、”、“” ReserveFlight、“” 和 “ReserveCarRental” 中的故障ProcessPayment,从而控制旅行预订系统的功能ConfirmCarRental。ConfirmFlight
saga
Lambda 函数 (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 航班表中插入一条带有
pending
的transaction_status
的记录以预订航班。确认航班:更新 DynamoDB 航班表中的记录,将
transaction_status
设置为confirmed
,以确认航班。取消航班预订:从 DynamoDB 航班表内删除记录,以取消待处理的航班。
预订租车:在 Dynamo CarRentals DB 表中插入一条带有
transaction_status
apending
的记录以预订租车。确认租车:更新 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消息,向客户通知预订成功。
预订失败
此流程是 saga 模式失败的一个例子。如果在预订航班和租车后,“ProcessPayment” 失败,则按相反的顺序取消步骤。 取消预订,并通过向该SNS主题的订阅者发送SMS消息将失败通知客户。