

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

# 现代化
<a name="modernization-pattern-list"></a>

**Topics**
+ [使用 DynamoDB TTL 自动将项目归档到 Amazon S3](automatically-archive-items-to-amazon-s3-using-dynamodb-ttl.md)
+ [在 Amazon 服务中构建多租户无服务器架构 OpenSearch](build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.md)
+ [使用 AWS CDK 部署多堆栈应用程序 TypeScript](deploy-multiple-stack-applications-using-aws-cdk-with-typescript.md)
+ [使用 AWS SAM 自动部署嵌套应用程序](automate-deployment-of-nested-applications-using-aws-sam.md)
+ [使用 AWS Lambda 代币自动售货机对 Amazon S3 实施 SaaS 租户隔离](implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine.md)
+ [使用 AWS Step Functions 实施无服务器 saga 模式](implement-the-serverless-saga-pattern-by-using-aws-step-functions.md)
+ [通过使用 AWS CDK 设置 Amazon ECS Anywhere 来管理本地容器应用程序](manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk.md)
+ [在 AWS 上实现 ASP.NET Web 表单应用程序的现代化](modernize-asp-net-web-forms-applications-on-aws.md)
+ [使用 C\$1 和 AWS CDK 在 SaaS 架构中为孤岛模型进行租户登录](tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.md)
+ [使用 CQRS 和事件溯源将整体分解为微服务](decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.md)
+ [更多模式](modernization-more-patterns-pattern-list.md)

# 使用 DynamoDB TTL 自动将项目归档到 Amazon S3
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl"></a>

*Tabby Ward，Amazon Web Services*

## Summary
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-summary"></a>

此模式提供了从 Amazon DynamoDB 表中删除较旧的数据并将其存档到 Amazon Web Services（AWS）上的 Amazon Simple Storage Service（Amazon S3）存储桶的步骤，而无需管理服务器实例集。 

此模式使用 Amazon DynamoDB 生存时间（TTL）自动删除旧项目，使用 Amazon DynamoDB Streams 来捕获 TTL 过期项目。然后，它将 DynamoDB Streams 连接到 AWS Lambda，后者无需预调配或管理任何服务器即可运行代码。 

向 DynamoDB 流中添加新项目时，Lambda 函数将启动并将数据写入 Amazon Data Firehose 传输流。Firehose 提供了一种简单、完全托管的解决方案，可将数据作为存档加载到 Amazon S3 中。

DynamoDB 通常用于存储时间序列数据，例如来自传感器和联网设备的网页点击流数据或物联网（IoT）数据。许多客户不想删除访问频率较低的项目，而是将其存档以供审计。TTL 根据时间戳属性自动删除项目，从而简化了存档。 

DynamoDB Streams 中可以识别由 TTL 删除的项目，DynamoDB Streams 可捕获按时间排序的项目级修改序列，并将该序列存储在日志中长达 24 个小时。这些数据可由 Lambda 函数使用并存档在 Amazon S3 存储桶中，以降低存储成本。[为了进一步降低成本，可以创建 [Amazon S3 生命周期规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)，自动将数据（一旦创建）转换为成本最低的存储类别。](https://aws.amazon.com/s3/storage-classes/)

## 先决条件和限制
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-prereqs"></a>

**先决条件**
+ 一个活跃的 AWS 账户。
+ [AWS 命令行界面（AWS CLI）1.7 或更高版本](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html)，已在 macOS、Linux 或 Windows 上安装并配置。
+ [Python 3.7](https://www.python.org/downloads/release/python-370/) 或更高版本。
+ [Boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)，已安装并配置。如果Boto3 尚未安装，请运行 `python -m pip install boto3` 命令进行安装。

## 架构
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-architecture"></a>

**技术堆栈**
+ Amazon DynamoDB
+ Amazon DynamoDB Streams
+ Amazon Data Firehose
+ AWS Lambda
+ Amazon S3

![\[从 DynamoDB 到 S3 存储桶的四步流程。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9dbc833f-cf3c-4574-8f09-d0b81134fe41/images/50d9da65-5398-4a99-bc8f-58afc80e9d7b.png)


1. TTL 会删除项目。

1. DynamoDB 流触发器调用 Lambda 流处理器函数。

1. Lambda 函数以批处理格式将记录放入 Firehose 传输流中。

1. 数据记录存档在 S3 存储桶中。

## 工具
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-tools"></a>
+ [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) – AWS 命令行界面（AWS CLI）是用于管理 Amazon Web Services 的统一工具。
+ [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) – Amazon DynamoDB 是一个键值和文档数据库，在任何规模上都能提供个位数的毫秒性能。
+ [Amazon DynamoDB 生存时间（TTL）](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html)– Amazon DynamoDB TTL 可以定义每个项目的时间戳，以确定何时不再需要某个项目。
+ [Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Types_Amazon_DynamoDB_Streams.html) – Amazon DynamoDB Streams 可在任何 DynamoDB 表中捕获按时间排序的项目级修改序列，并将这类信息存储在日志中长达 24 个小时。
+ [Amazon Data Firehose](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html) – Amazon Data Firehose 是将流数据可靠地加载到数据湖、数据存储和分析服务的最简单方法。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) – AWS Lambda 无需预调配或管理服务器即可运行代码。您只需按使用的计算时间付费。
+ [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html) – Amazon Simple Storage Service（Amazon S3）是一种对象存储服务，提供行业领先的可扩展性、数据可用性、安全性和性能。

**代码**

此模式的代码可在[使用 DynamoDB TTL 存储库将项目 GitHub 存档到 S3](https://github.com/aws-samples/automatically-archive-items-to-s3-using-dynamodb-ttl) 中找到。

## 操作说明
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-epics"></a>

### 设置 DynamoDB 表、TTL 和 DynamoDB 流
<a name="set-up-a-dynamodb-table-ttl-and-a-dynamodb-stream"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 DynamoDB 表。 | 使用 AWS CLI 在 DynamoDB 中创建一个名为 `Reservation`的表。选择随机读取容量单位（RCU）和写入容量单位（WCU），并给表两个属性：`ReservationID` 和 `ReservationDate`。 <pre>aws dynamodb create-table \<br />--table-name Reservation \<br />--attribute-definitions AttributeName=ReservationID,AttributeType=S AttributeName=ReservationDate,AttributeType=N \<br />--key-schema AttributeName=ReservationID,KeyType=HASH AttributeName=ReservationDate,KeyType=RANGE \<br />--provisioned-throughput ReadCapacityUnits=100,WriteCapacityUnits=100 </pre>`ReservationDate` 是一个纪元时间戳，将用于开启 TTL。 | 云架构师、应用程序开发人员 | 
| 开启 DynamoDB TTL。 | 使用 AWS CLI 为 `ReservationDate` 属性开启 DynamoDB TTL。<pre>aws dynamodb update-time-to-live \<br />--table-name Reservation\<br />  --time-to-live-specification Enabled=true,AttributeName=ReservationDate</pre> | 云架构师、应用程序开发人员 | 
| 开启 DynamoDB 流。 | 使用 AWS CLI 通过 `NEW_AND_OLD_IMAGES` 流类型为 `Reservation` 表打开 DynamoDB 流。 <pre>aws dynamodb update-table \<br />--table-name Reservation \<br />  --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES</pre>此流将包含新项目、更新项目、已删除项目和由 TTL 删除的项目的记录。由 TTL 删除的项目的记录包含一个额外的元数据属性，用于将其与手动删除的项目区分开来。TTL 删除的 `userIdentity` 字段表示 DynamoDB 服务执行了删除操作。 在这种模式中，只有通过 TTL 删除的项目才会被存档，但只能存档 `eventName` 为 `REMOVE` 以及 `userIdentity` 包含等于 `dynamodb.amazonaws.com` 的 `principalId` 的记录。 | 云架构师、应用程序开发人员 | 

### 创建和配置 S3 存储桶
<a name="create-and-configure-an-s3-bucket"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 S3 存储桶。 | 使用 AWS CLI 在您的 AWS 区域中创建目标 S3 存储桶，`us-east-1`替换为您的区域，将 amzn-s3-demo-destination-bucket 替换为存储桶的名称。 <pre>aws s3api create-bucket \<br />--bucket amzn-s3-demo-destination-bucket \<br />--region us-east-1</pre>确保 S3 存储桶名称是全局唯一的，因为命名空间由所有 AWS 账户共享。 | 云架构师、应用程序开发人员 | 
| 为 S3 存储桶创建 30 天的生命周期策略。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/automatically-archive-items-to-amazon-s3-using-dynamodb-ttl.html) | 云架构师、应用程序开发人员 | 

### 创建 Firehose 传输流
<a name="create-a-akf-delivery-stream"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建并配置 Firehose 传输流。 | 从 GitHub 存储库下载并编辑`CreateFireHoseToS3.py`代码示例。 此代码使用 Python 编写，向您展示如何创建 Firehose 传输流和 AWS Identity and Access Management（IAM）角色。IAM 角色将有一个策略，Firehose 可以使用该策略写入目标 S3 存储桶。要运行该脚本，使用以下命令和命令行参数。参数 1= `<Your_S3_bucket_ARN>`，这是您之前创建的存储桶的 Amazon 资源名称（ARN）参数 2= Firehose 名称（此试点正在使用 `firehose_to_s3_stream`。）参数 3= IAM 角色名称（此试点正在使用 `firehose_to_s3`。）<pre>python CreateFireHoseToS3.py <Your_S3_Bucket_ARN> firehose_to_s3_stream firehose_to_s3</pre>如果指定的 IAM 角色不存在，则该脚本将创建一个具有可信关系策略和授予充足 Amazon S3 权限的策略的分派角色。有关这些政策的示例，请参阅*其他信息*部分。 | 云架构师、应用程序开发人员 | 
| 验证 Firehose 传输流。 | 通过使用 AWS CLI 描述 Firehose 传输流，以验证该传输流是否已成功创建。<pre>aws firehose describe-delivery-stream --delivery-stream-name firehose_to_s3_stream </pre> | 云架构师、应用程序开发人员 | 

### 创建 Lambda 函数来处理 Firehose 传输流
<a name="create-a-lambda-function-to-process-the-akf-delivery-stream"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 Lambda 函数的信任策略。 | 请使用以下信息创建信任策略文件。<pre> {<br />     "Version": "2012-10-17",		 	 	 <br />     "Statement": [<br />      {<br />          "Effect": "Allow",<br />          "Principal": {<br />              "Service": "lambda.amazonaws.com"<br />           },<br />           "Action": "sts:AssumeRole"<br />      }<br />    ]<br />  } </pre>这向您的函数授予访问 AWS 资源的权限。 | 云架构师、应用程序开发人员 | 
| 创建 Lambda 函数的执行角色。 | 要创建执行角色，请运行以下代码。<pre>aws iam create-role --role-name lambda-ex --assume-role-policy-document file://TrustPolicy.json</pre> | 云架构师、应用程序开发人员 | 
| 向角色添加权限。 | 要向角色添加权限，请使用 `attach-policy-to-role` 命令。<pre>aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole<br />aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole<br />aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/AmazonKinesisFirehoseFullAccess<br />aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/IAMFullAccess </pre> | 云架构师、应用程序开发人员 | 
| 创建一个 Lambda 函数。 | 通过运行以下命令从代码存储库中压缩 `LambdaStreamProcessor.py` 文件。<pre>zip function.zip LambdaStreamProcessor.py</pre>当创建 Lambda 函数时，您将需要 Lambda 执行角色 ARN。要获取 ARN，请运行以下代码。<pre>aws iam get-role \<br />--role-name lambda-ex </pre>要创建 Lambda 函数，请运行以下代码。<pre># Review the environment variables and replace them with your values.<br /><br />aws lambda create-function --function-name LambdaStreamProcessor \<br />--zip-file fileb://function.zip --handler LambdaStreamProcessor.handler --runtime python3.8 \<br />--role {Your Lamda Execution Role ARN}\<br />  --environment Variables="{firehose_name=firehose_to_s3_stream,bucket_arn = <Your_S3_bucket_ARN>,iam_role_name = firehose_to_s3, batch_size=400}"</pre> | 云架构师、应用程序开发人员 | 
| 配置 Lambda 函数触发器。 | 使用 AWS CLI 配置触发器（DynamoDB Streams），该触发器将调用 Lambda 函数。批量大小为 400 是为了避免遇到 Lambda 并发问题。<pre>aws lambda create-event-source-mapping --function-name LambdaStreamProcessor \<br />--batch-size 400 --starting-position LATEST \<br />--event-source-arn <Your Latest Stream ARN From DynamoDB Console></pre> | 云架构师、应用程序开发人员 | 

### 测试功能
<a name="test-the-functionality"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 将时间戳已过期的项目添加到预留表。 | 要测试该功能，请将带有过期纪元时间戳的项目添加到 `Reservation` 表中。TTL 将根据时间戳自动删除项目。 Lambda 函数是在 DynamoDB Stream 活动时启动的，它会筛选事件以识别 `REMOVE` 活动或已删除的项目。然后，它以批处理格式将记录放入 Firehose 传输流中。Firehose 传输流将项目传输到带有 `firehosetos3example/year=current year/month=current month/ day=current day/hour=current hour/` 前缀的目标 S3 存储桶。要优化数据检索，请使用 `Prefix` 和 `ErrorOutputPrefix` 配置 Amazon S3，详见*其他信息*部分。 | 云架构师  | 

### 清理资源
<a name="clean-up-the-resources"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 删除所有资源。 | 删除所有资源，确保不会为未使用的任何服务付费。  | 云架构师、应用程序开发人员 | 

## 相关资源
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-resources"></a>
+ [管理存储生命周期](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/create-lifecycle.html)
+ [Amazon S3 存储类](https://aws.amazon.com/s3/storage-classes/)
+ [适用于 Python 的 Amazon SDK（Boto3）文档](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)

## 附加信息
<a name="automatically-archive-items-to-amazon-s3-using-dynamodb-ttl-additional"></a>

**创建并配置 Firehose 传输流 – 策略示例**

*Firehose 可信关系策略示例文档*

```
firehose_assume_role = {
        'Version': '2012-10-17',
        'Statement': [
            {
                'Sid': '',
                'Effect': 'Allow',
                'Principal': {
                    'Service': 'firehose.amazonaws.com'
                },
                'Action': 'sts:AssumeRole'
            }
        ]
    }
```

*S3 权限策略示例*

```
s3_access = {
        "Version": "2012-10-17",		 	 	 
        "Statement": [
            {
                "Sid": "",
                "Effect": "Allow",
                "Action": [
                    "s3:AbortMultipartUpload",
                    "s3:GetBucketLocation",
                    "s3:GetObject",
                    "s3:ListBucket",
                    "s3:ListBucketMultipartUploads",
                    "s3:PutObject"
                ],
                "Resource": [
                    "{your s3_bucket ARN}/*",
                    "{Your s3 bucket ARN}"
                ]
            }
        ]
    }
```

**测试功能 – Amazon S3 配置**

选择具有以下 `Prefix` 和 `ErrorOutputPrefix` 前缀的 Amazon S3 配置来优化数据检索。 

*prefix*

```
firehosetos3example/year=! {timestamp: yyyy}/month=! {timestamp:MM}/day=! {timestamp:dd}/hour=!{timestamp:HH}/
```

Firehose 首先直接在 S3 存储桶下创建名为 `firehosetos3example` 的基本文件夹。然后，它使用 Java [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)格式`!{timestamp:HH}`将表达式`!{timestamp:yyyy}``!{timestamp:MM}``!{timestamp:dd}`、、和计算为年、月、日和小时。

例如，在 Unix 纪元时间中，1604683577 的近似到达时间戳的评估结果为 `year=2020`、`month=11`、`day=06` 和 `hour=05`。因此，Amazon S3 中数据记录的传输位置评估为 `firehosetos3example/year=2020/month=11/day=06/hour=05/`。

*ErrorOutputPrefix*

```
firehosetos3erroroutputbase/!{firehose:random-string}/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd}/
```

`ErrorOutputPrefix` 结果会直接在 S3 存储桶下生成名为 `firehosetos3erroroutputbase` 的基本文件夹。表达式 `!{firehose:random-string}` 的评估结果为 11 个字符的随机字符串，例如 `ztWxkdg3Thg`。传输失败记录的 Amazon S3 对象的位置可以评估为 `firehosetos3erroroutputbase/ztWxkdg3Thg/processing-failed/2020/11/06/`。

# 在 Amazon 服务中构建多租户无服务器架构 OpenSearch
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service"></a>

*Tabby Ward 和 Nisha Gambhir，Amazon Web Services*

## Summary
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-summary"></a>

Amazon S OpenSearch ervice 是一项托管服务，可以轻松部署、操作和扩展 Elasticsearch，这是一款流行的开源搜索和分析引擎。 OpenSearch 该服务为日志和指标等流式数据提供自由文本搜索以及近乎实时的摄取和仪表板管理。

软件即服务 (SaaS) 提供商经常使用 OpenSearch 服务来解决各种用例，例如以可扩展和安全的方式获取客户见解，同时降低复杂性和停机时间。

在多租户环境中使用 OpenSearch 服务会引入一系列注意事项，这些注意事项会影响 SaaS 解决方案的分区、隔离、部署和管理。SaaS 提供程序必须考虑如何随着不断变化的工作负载有效扩展其 Elasticsearch 集群。他们还需要考虑分层和嘈杂的邻居条件如何影响他们的分区模型。

此模式介绍了用于通过 Elasticsearch 构造表示和隔离租户数据的模型。此外，该模式以简单的无服务器参考架构为例，演示在多租户环境中使用 Serv OpenSearch ice 进行索引和搜索。它实现了池数据分区模型，在所有租户之间共享相同的索引，同时保持租户的数据隔离。此模式使用以下 AWS 服务：亚马逊 API Gateway AWS Lambda、亚马逊简单存储服务 (Amazon S3) Service 和服务。 OpenSearch 

有关池模型和其他数据分区模型的更多信息，请参阅[其他信息](#build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-additional)部分。

## 先决条件和限制
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-prereqs"></a>

**先决条件**
+ 活跃的 AWS 账户
+ [AWS Command Line Interface (AWS CLI) 版本 2.x](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)，已在 macOS、Linux 或 Windows 上安装和配置
+ [Python 版本 3.9](https://www.python.org/downloads/release/python-3921/)
+ [pip3](https://pip.pypa.io/en/stable/) — Python 源代码以.zip 文件形式提供，要部署至 Lambda 函数中。若要在本地使用代码或对其进行自定义，请按照以下步骤开发和重新编译源代码：

  1. 通过在与 Python 脚本相同的目录中运行以下命令以生成 `requirements.txt` 文件：`pip3 freeze > requirements.txt`

  1. 安装依赖项：`pip3 install -r requirements.txt`

**限制**
+ 此代码在 Python 中运行，当前不支持其他编程语言。 
+ 示例应用程序不包括 AWS 跨区域或灾难恢复 (DR) 支持。 
+ 此模式仅用于演示目的。不应在生产环境中使用。

## 架构
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-architecture"></a>

下图展示了此模式的高级架构。该架构包括以下内容：
+ Lambda，用于对内容进行索引和查询 
+ OpenSearch 执行搜索的服务 
+ API Gateway，用于提供与用户的 API 交互
+ Amazon S3，用于存储原始（未编入索引）的数据
+ 亚马逊 CloudWatch 将监控日志
+ AWS Identity and Access Management (IAM) 用于创建租户角色和策略

![\[高级多租户无服务器架构。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/1a8501e7-0776-4aca-aed3-28e3ada1d15d.png)


**自动化和扩展**

为简单起见，该模式 AWS CLI 用于配置基础架构和部署示例代码。您可以创建一个 CloudFormation 或多个 AWS Cloud Development Kit (AWS CDK) 脚本来自动执行该模式。

## 工具
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-tools"></a>

**AWS 服务**
+ [AWS CLI](https://aws.amazon.com/cli/)是一个统一的工具，用于通过在命令行外壳中使用命令来管理和 AWS 服务 资源。
+ [Lambda](https://aws.amazon.com/lambda/) 是一项计算服务，可使您无需预调配或管理服务器即可运行代码。只有在需要时 Lambda 才运行您的代码，并且能自动扩展，从每天几个请求扩展到每秒数千个请求。
+ [API Gateway](https://aws.amazon.com/api-gateway/) AWS 服务 用于创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [Amazon S3](https://aws.amazon.com/s3/) 是一种对象存储服务，可让您随时从任何位置在 Web 上存储和检索任意数量的信息。
+ [OpenSearch 服务](https://aws.amazon.com/opensearch-service/)是一项完全托管的服务，可让您轻松地大规模部署、保护和运行 Elasticsearch，经济实惠。

**代码**

附件提供了此模式的示例文件。这些方法包括：
+ `index_lambda_package.zip`— Lambda 函数，用于使用池模型对 OpenSearch 服务中的数据进行索引。
+ `search_lambda_package.zip`— 用于在服务中搜索数据的 Lambda 函数。 OpenSearch 
+ `Tenant-1-data` ‒ Tenant-1 的原始（非索引）数据示例。
+ `Tenant-2-data` ‒ Tenant-2 的原始（非索引）数据示例。

**重要**  
这种模式中的故事包括针对 Unix、Linux 和 macOS 进行格式化的 AWS CLI 命令示例。对于 Windows，请将每行末尾的反斜杠 (\$1) Unix 行继续符替换为脱字号 (^)。

**注意**  
在 AWS CLI 命令中，将尖括号 (<>) 内的所有值替换为正确的值。

## 操作说明
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-epics"></a>

### 创建和配置 S3 存储桶
<a name="create-and-configure-an-s3-bucket"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 S3 存储桶。 | 在您的中创建 S3 存储桶 AWS 区域。此存储桶将保存示例应用程序未编入索引的租户数据。确保 S3 存储桶的名称是全球唯一的，因为命名空间由所有人共享 AWS 账户。要创建 S3 存储桶，您可以按如下方式使用 AWS CLI [create-bu](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/create-bucket.html) cket 命令：<pre>aws s3api create-bucket \<br />  --bucket <tenantrawdata> \<br />  --region <your-AWS-Region></pre>其中 `tenantrawdata` 是 S3 存储桶名称。(您可使用任何符合[存储桶命名准则的](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)唯一名称。) | 云架构师、云管理员 | 

### 创建和配置 Elasticsearch 集群
<a name="create-and-configure-an-elasticsearch-cluster"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 OpenSearch 服务域。 | 运行 AWS CLI [create-elasticsearch-domain](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/es/create-elasticsearch-domain.html)命令创建 OpenSearch 服务域：<pre>aws es create-elasticsearch-domain \<br />  --domain-name vpc-cli-example \<br />  --elasticsearch-version 7.10 \<br />  --elasticsearch-cluster-config InstanceType=t3.medium.elasticsearch,InstanceCount=1 \<br />  --ebs-options EBSEnabled=true,VolumeType=gp2,VolumeSize=10 \<br />  --domain-endpoint-options "{\"EnforceHTTPS\": true}" \<br />  --encryption-at-rest-options "{\"Enabled\": true}" \<br />  --node-to-node-encryption-options "{\"Enabled\": true}" \<br />  --advanced-security-options "{\"Enabled\": true, \"InternalUserDatabaseEnabled\": true, \<br />    \"MasterUserOptions\": {\"MasterUserName\": \"KibanaUser\", \<br />    \"MasterUserPassword\": \"NewKibanaPassword@123\"}}" \<br />  --vpc-options "{\"SubnetIds\": [\"<subnet-id>\"], \"SecurityGroupIds\": [\"<sg-id>\"]}" \<br />  --access-policies "{\"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \ <br />    \"Principal\": {\"AWS\": \"*\" }, \"Action\":\"es:*\", \<br />    \"Resource\": \"arn:aws:es:<region>:<account-id>:domain\/vpc-cli-example\/*\" } ] }"</pre>实例计数设置为 1，因为该域用于测试。您需要使用 `advanced-security-options` 参数启用精细的访问控制，因为创建域后无法更改详细信息。 此命令会创建主用户名 (`KibanaUser`) 和密码，您可使用这些用户名和密码登录 Kibana 控制台。由于该域是虚拟私有云（VPC）的组成部分，因此您必须通过指定要使用的访问策略来确保可以访问 Elasticsearch 实例。有关更多信息，请参阅 AWS 文档中的在 [VPC 内启动您的 Amazon OpenSearch 服务域](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html)。 | 云架构师、云管理员 | 
| 设置堡垒主机。 | 将亚马逊弹性计算云 (亚马逊 EC2) Windows 实例设置为访问 Kibana 控制台的堡垒主机。Elasticsearch 安全组必须允许来自亚马逊 EC2 安全组的流量。有关说明，请参阅博客文章[使用堡垒服务器控制对 EC2 实例的网络访问](https://aws.amazon.com/blogs/security/controlling-network-access-to-ec2-instances-using-a-bastion-server/)。设置堡垒主机并且您拥有与可用实例关联的安全组后，请使用 AWS CLI [authorize-security-group-ingress](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/authorize-security-group-ingress.html)命令向 Elasticsearch 安全组添加权限，允许从 Amazon EC2 （堡垒主机）安全组中访问 443 端口。<pre>aws ec2 authorize-security-group-ingress \<br />  --group-id <SecurityGroupIdfElasticSearch> \ <br />  --protocol tcp \<br />  --port 443 \<br />  --source-group <SecurityGroupIdfBashionHostEC2></pre> | 云架构师、云管理员 | 

### 创建并配置 Lambda 索引函数
<a name="create-and-configure-the-lam-index-function"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 Lambda 执行角色。 | 运行 AWS CLI [create-role](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-role.html) 命令以授予 Lambda 索引函数访问和资源的权限： AWS 服务 <pre>aws iam create-role \<br />  --role-name index-lambda-role \<br />  --assume-role-policy-document file://lambda_assume_role.json</pre>其中 `lambda_assume_role.json` 是用于授予 Lambda 函数 `AssumeRole` 权限的 JSON 文档，如下所示：<pre>{<br />     "Version": "2012-10-17",		 	 	 <br />     "Statement": [<br />         {<br />             "Effect": "Allow",<br />             "Principal": {<br />                 "Service": "lambda.amazonaws.com"<br />               },<br />             "Action": "sts:AssumeRole"<br />         }<br />     ]<br /> }</pre> | 云架构师、云管理员 | 
| 将托管式策略附加到 Lambda 角色。 | 运行 AWS CLI [attach-role-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/attach-role-policy.html)命令将托管策略附加到上一步中创建的角色。这两个策略授予角色创建弹性网络 interface 和向日志写入 CloudWatch 日志的权限。<pre>aws iam attach-role-policy \<br />  --role-name index-lambda-role \<br />  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole<br /><br />aws iam attach-role-policy \<br />  --role-name index-lambda-role \<br />  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole </pre> | 云架构师、云管理员 | 
| 创建策略，以授予 Lambda 索引函数读取 S3 对象的权限。 | 运行 AWS CLI [create-pol](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-policy.html) icy 命令以授予 Lambda 索引函数读取 S3 存储桶中对象的`s3:GetObject`权限：<pre>aws iam create-policy \<br />  --policy-name s3-permission-policy \<br />  --policy-document file://s3-policy.json</pre>文件 `s3-policy.json` 是下面显示的 JSON 文档，它授予允许 `s3:GetObject` 对 S3 对象进行读取访问的权限。如果创建 S3 存储桶时使用了其他名称，请在 `Resource ` 部分中提供正确的存储桶名称：<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />           "Effect": "Allow",<br />           "Action": "s3:GetObject",<br />           "Resource": "arn:aws:s3:::<tenantrawdata>/*"<br />        }<br />    ]<br />}</pre> | 云架构师、云管理员 | 
| 将 Amazon S3 权限策略附加至 Lambda 执行角色。 | 运行 AWS CLI [attach-role-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/attach-role-policy.html)命令将您在上一步中创建的 Amazon S3 权限策略附加到 Lambda 执行角色：<pre>aws iam attach-role-policy \<br />  --role-name index-lambda-role \<br />  --policy-arn <PolicyARN></pre>其中 `PolicyARN` 是 Amazon S3 权限策略的 Amazon 资源名称（ARN）。您可从上一条命令的输出中获得此值。 | 云架构师、云管理员 | 
| 创建 Lambda 索引函数。 | 运行 AWS CLI [create-fun](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) ction 命令创建 Lambda 索引函数，该函数将访问服务： OpenSearch <pre>aws lambda create-function \<br />  --function-name index-lambda-function \<br />  --zip-file fileb://index_lambda_package.zip \<br />  --handler lambda_index.lambda_handler \<br />  --runtime python3.9 \<br />  --role "arn:aws:iam::account-id:role/index-lambda-role" \<br />  --timeout 30 \<br />  --vpc-config "{\"SubnetIds\": [\"<subnet-id1\>", \"<subnet-id2>\"], \<br />    \"SecurityGroupIds\": [\"<sg-1>\"]}"</pre> | 云架构师、云管理员 | 
| 允许 Amazon S3 调用 Lambda 索引函数。 | 运行 add-p AWS CLI [ermission 命令授](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/add-permission.html)予 Amazon S3 调用 Lambda 索引函数的权限：<pre>aws lambda add-permission \<br />  --function-name index-lambda-function \<br />  --statement-id s3-permissions \<br />  --action lambda:InvokeFunction \<br />  --principal s3.amazonaws.com \<br />  --source-arn "arn:aws:s3:::<tenantrawdata>" \<br />  --source-account "<account-id>" </pre> | 云架构师、云管理员 | 
| 为 Amazon S3 事件添加 Lambda 触发器。 | 运行 AWS CLI [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html)命令以在检测到 Amazon S3 `ObjectCreated` 事件时向 Lambda 索引函数发送通知。每当将对象上传至 S3 存储桶时，索引函数就会运行。 <pre>aws s3api put-bucket-notification-configuration \<br />  --bucket <tenantrawdata> \<br />  --notification-configuration file://s3-trigger.json</pre>文件 `s3-trigger.json` 是当前文件夹中的一个 JSON 文档，用于在 Amazon S3 `ObjectCreated` 事件发生时向 Lambda 函数添加资源策略。 | 云架构师、云管理员 | 

### 创建并配置 Lambda 搜索函数
<a name="create-and-configure-the-lam-search-function"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 Lambda 执行角色。 | 运行 AWS CLI [create-role](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-role.html) 命令以授予 Lambda 搜索函数访问和资源的权限： AWS 服务 <pre>aws iam create-role \<br />  --role-name search-lambda-role \<br />  --assume-role-policy-document file://lambda_assume_role.json</pre>其中 `lambda_assume_role.json` 是当前文件夹中用于授予 Lambda 函数 `AssumeRole` 权限的 JSON 文档，如下所示：<pre>{<br />     "Version": "2012-10-17",		 	 	 <br />     "Statement": [<br />         {<br />             "Effect": "Allow",<br />             "Principal": {<br />                 "Service": "lambda.amazonaws.com"<br />               },<br />             "Action": "sts:AssumeRole"<br />         }<br />     ]<br /> }</pre> | 云架构师、云管理员 | 
| 将托管式策略附加到 Lambda 角色。 | 运行 AWS CLI [attach-role-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/attach-role-policy.html)命令将托管策略附加到上一步中创建的角色。这两个策略授予角色创建弹性网络 interface 和向日志写入 CloudWatch 日志的权限。<pre>aws iam attach-role-policy \<br />  --role-name search-lambda-role \<br />  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole<br /><br />aws iam attach-role-policy \<br />  --role-name search-lambda-role \<br />  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole </pre> | 云架构师、云管理员 | 
| 创建 Lambda 搜索函数。 | 运行 AWS CLI [create-fun](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) ction 命令创建 Lambda 搜索函数，该函数将访问服务： OpenSearch <pre>aws lambda create-function \<br />  --function-name search-lambda-function \<br />  --zip-file fileb://search_lambda_package.zip \<br />  --handler lambda_search.lambda_handler \<br />  --runtime python3.9 \<br />  --role "arn:aws:iam::account-id:role/search-lambda-role" \<br />  --timeout 30 \<br />  --vpc-config "{\"SubnetIds\": [\"<subnet-id1\>", \"<subnet-id2>\"], \<br />    \"SecurityGroupIds\": [\"<sg-1>\"]}"</pre> | 云架构师、云管理员 | 

### 创建和配置租户角色
<a name="create-and-configure-tenant-roles"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建租户 IAM 角色。 | 运行 AWS CLI [create-](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-role.html) role 命令创建两个用于测试搜索功能的租户角色：<pre>aws iam create-role \<br />  --role-name Tenant-1-role \<br />  --assume-role-policy-document file://assume-role-policy.json</pre><pre>aws iam create-role \<br />  --role-name Tenant-2-role \<br />  --assume-role-policy-document file://assume-role-policy.json</pre>文件 `assume-role-policy.json` 是当前文件夹中的一个 JSON 文档，用于向 Lambda 执行角色授予 `AssumeRole` 权限：<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Effect": "Allow",<br />            "Principal": {<br />                 "AWS": "<Lambda execution role for index function>",<br />                 "AWS": "<Lambda execution role for search function>"<br />             },<br />            "Action": "sts:AssumeRole"<br />        }<br />    ]<br />}</pre> | 云架构师、云管理员 | 
| 创建租户 IAM 策略。 | 运行 AWS CLI [create-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-policy.html) 命令来创建授予对 Elasticsearch 操作的访问权限的租户策略：<pre>aws iam create-policy \<br />  --policy-name tenant-policy \<br />  --policy-document file://policy.json</pre>文件 `policy.json` 是当前文件夹中授予对 Elasticsearch 的权限的 JSON 文档：<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Effect": "Allow",<br />            "Action": [<br />                "es:ESHttpDelete",<br />                "es:ESHttpGet",<br />                "es:ESHttpHead",<br />                "es:ESHttpPost",<br />                "es:ESHttpPut",<br />                "es:ESHttpPatch"<br />            ],<br />            "Resource": [<br />                "<ARN of Elasticsearch domain created earlier>"<br />            ]<br />        }<br />    ]<br />}</pre> | 云架构师、云管理员 | 
| 将租户 IAM policy 附加至租户角色。 | 运行 AWS CLI [attach-role-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/attach-role-policy.html)命令将租户 IAM 策略附加到您在前面的步骤中创建的两个租户角色：<pre>aws iam attach-role-policy \<br />  --policy-arn arn:aws:iam::account-id:policy/tenant-policy \<br />  --role-name Tenant-1-role<br /><br />aws iam attach-role-policy \<br />  --policy-arn arn:aws:iam::account-id:policy/tenant-policy \<br />  --role-name Tenant-2-role</pre>策略 ARN 来自上一步中的输出。 | 云架构师、云管理员 | 
| 创建 IAM 策略，以向 Lambda 授予代入角色的权限。 | 运行 AWS CLI [create-pol](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-policy.html) icy 命令为 Lambda 创建担任租户角色的策略：<pre>aws iam create-policy \<br />  --policy-name assume-tenant-role-policy \<br />  --policy-document file://lambda_policy.json</pre>文件 `lambda_policy.json` 是当前文件夹中的一个 JSON 文档，用于授予对 `AssumeRole` 的权限。<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />       {<br />            "Effect": "Allow",<br />            "Action":  "sts:AssumeRole",<br />            "Resource": "<ARN of tenant role created earlier>"<br />       }<br />    ]<br />}</pre>对于 `Resource`，您可使用通配符来避免为每个租户创建新策略。 | 云架构师、云管理员 | 
| 创建 IAM 策略，向 Lambda 索引角色授予 Amazon S3 访问权限。 | 运行 AWS CLI [create-pol](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-policy.html) icy 命令以授予 Lambda 索引角色访问 S3 存储桶中对象的权限：<pre>aws iam create-policy \<br />  --policy-name s3-permission-policy \<br />  --policy-document file://s3_lambda_policy.json</pre>文件 `s3_lambda_policy.json` 是当前文件夹中以下 JSON 策略文档：<pre>{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Effect": "Allow",<br />            "Action": "s3:GetObject",<br />            "Resource": "arn:aws:s3:::tenantrawdata/*"<br />        }<br />    ]<br />}</pre> | 云架构师、云管理员 | 
| 将该策略附加到 Lambda 执行角色。 | 运行 AWS CLI [attach-role-policy](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/attach-role-policy.html)命令将上一步中创建的策略附加到您之前创建的 Lambda 索引和搜索执行角色：<pre>aws iam attach-role-policy \<br />  --policy-arn arn:aws:iam::account-id:policy/assume-tenant-role-policy \<br />  --role-name index-lambda-role<br /><br />aws iam attach-role-policy \<br />  --policy-arn arn:aws:iam::account-id:policy/assume-tenant-role-policy \<br />  --role-name search-lambda-role<br /><br />aws iam attach-role-policy \<br />  --policy-arn arn:aws:iam::account-id:policy/s3-permission-policy \<br />  --role-name index-lambda-role</pre>策略 ARN 来自上一步中的输出。 | 云架构师、云管理员 | 

### 创建和配置搜索 API
<a name="create-and-configure-a-search-api"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 在 API Gateway 中创建 REST API。 | 运行 AWS CLI [create-rest-api](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/create-rest-api.html)命令创建 REST API 资源：<pre>aws apigateway create-rest-api \<br />  --name Test-Api \<br />  --endpoint-configuration "{ \"types\": [\"REGIONAL\"] }"</pre>对于端点配置类型，您可指定 `EDGE`（而不是 `REGIONAL`）使用边缘站点，而不是某个 AWS 区域。记录命令输出中 `id` 字段的值。这是您将在后续命令使用的 API ID。 | 云架构师、云管理员 | 
| 创建资源，以用于搜索 API。 | 搜索 API 资源使用名为 `search` 的资源启动 Lambda 搜索函数。（您不必为 Lambda 索引函数创建 API，因为当对象上传到 S3 存储桶时，它会自动运行。）[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html) | 云架构师、云管理员 | 
| 为搜索 API 创建 GET 方法。 | 运行 AWS CLI [put-met](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/put-method.html) hod 命令为搜索 API 创建`GET `方法：<pre>aws apigateway put-method \<br />  --rest-api-id <API-ID> \<br />  --resource-id <ID from the previous command output> \<br />  --http-method GET \<br />  --authorization-type "NONE" \<br />  --no-api-key-required</pre>对于 `resource-id`，请从 `create-resource` 命令输出中指定 ID。 | 云架构师、云管理员 | 
| 为搜索 API 创建响应方法。 | 运行 AWS CLI [put-method-response](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/put-method-response.html)命令为搜索 API 添加方法响应：<pre>aws apigateway put-method-response \<br />  --rest-api-id <API-ID> \<br />  --resource-id  <ID from the create-resource command output> \<br />  --http-method GET \<br />  --status-code 200 \<br />  --response-models "{\"application/json\": \"Empty\"}"</pre>对于 `resource-id`，请从先前 `create-resource` 命令的输出中指定 ID。 | 云架构师、云管理员 | 
| 为搜索 API 设置代理 Lambda 集成。 | 运行 AWS CLI [put-](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/put-integration.html) integration 命令设置与 Lambda 搜索函数的集成：<pre>aws apigateway put-integration \<br />  --rest-api-id <API-ID> \<br />  --resource-id  <ID from the create-resource command output> \<br />  --http-method GET \<br />  --type AWS_PROXY \<br />  --integration-http-method GET \<br />  --uri arn:aws:apigateway:region:lambda:path/2015-03-31/functions/arn:aws:lambda:<region>:<account-id>:function:<function-name>/invocations</pre>对于 `resource-id`，请指定先前 `create-resource` 命令中的 ID。 | 云架构师、云管理员 | 
| 授予 API Gateway 调用 Lambda 搜索函数的权限。 | 运行 add-p AWS CLI [ermission 命令授](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/add-permission.html)予 API Gateway 使用搜索功能的权限：<pre>aws lambda add-permission \<br />  --function-name <function-name> \<br />  --statement-id apigateway-get \<br />  --action lambda:InvokeFunction \<br />  --principal apigateway.amazonaws.com \<br />  --source-arn "arn:aws:execute-api:<region>:<account-id>:api-id/*/GET/search</pre>如果您使用其他名为 `search` 的 API 资源，则更改 `source-arn` 路径。 | 云架构师、云管理员 | 
| 部署搜索 API。 | 运行 AWS CLI [create-dep](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/create-deployment.html) loyment 命令创建名为：`dev`<pre>aws apigateway create-deployment \<br />  --rest-api-id <API-ID> \<br />  --stage-name dev</pre>如果您更新 API，则可以使用相同的 AWS CLI 命令将其重新部署到同一阶段。 | 云架构师、云管理员 | 

### 创建和配置 Kibana 角色
<a name="create-and-configure-kibana-roles"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 登录到 Kibana 控制台。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html) | 云架构师、云管理员 | 
| 创建和配置 Kibana 角色。 | 为了提供数据隔离并确保一个租户无法检索另一租户的数据，您需要使用文档安全性，它允许租户仅访问包含其租户 ID 的文档。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html) | 云架构师、云管理员 | 
| 将用户映射至角色。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html)我们建议您在租户入职时自动创建租户与 Kibana 角色。 | 云架构师、云管理员 | 
| 创建 tenant-data 索引。 | 在导航窗格的**管理**下，选择**开发工具**，然后运行以下命令。此命令创建 `tenant-data` 索引，以定义 `TenantId` 属性的映射。<pre>PUT /tenant-data<br />{<br />  "mappings": {<br />    "properties": {<br />      "TenantId": { "type": "keyword"}<br />    }<br />  }<br />}</pre> | 云架构师、云管理员 | 

### 为 Amazon S3 创建 VPC 终端节点和 AWS STS
<a name="create-vpc-endpoints-for-s3-and-sts"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 为 Amazon S3 创建和配置 VPC 端点。 | 运行 AWS CLI [create-vpc-endpoint](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/create-vpc-endpoint.html)命令为 Amazon S3 创建 VPC 终端节点。该端点允许 VPC 中的 Lambda 索引函数访问 Amazon S3。<pre>aws ec2 create-vpc-endpoint \<br />  --vpc-id <VPC-ID> \<br />  --service-name com.amazonaws.us-east-1.s3 \<br />  --route-table-ids <route-table-ID></pre>对于 `vpc-id`，请指定您用于 Lambda 索引函数的 VPC。对于 `service-name`，请使用 Amazon S3 端点的正确网址。对于 `route-table-ids`，请指定与 VPC 端点关联的路由表。 | 云架构师、云管理员 | 
| 为创建 VPC 终端节点 AWS STS。 | 运行 AWS CLI [create-vpc-endpoint](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/create-vpc-endpoint.html)命令为 AWS Security Token Service (AWS STS) 创建 VPC 终端节点。该端点允许 VPC 中的 Lambda 索引和搜索函数访问 AWS STS。这些函数在担任 IAM 角色 AWS STS 时使用。<pre>aws ec2 create-vpc-endpoint \<br />  --vpc-id <VPC-ID> \<br />  --vpc-endpoint-type Interface \<br />  --service-name com.amazonaws.us-east-1.sts \<br />  --subnet-id <subnet-ID> \<br />  --security-group-id <security-group-ID></pre>对于 `vpc-id`，请指定您用于 Lambda 索引和搜索功能的 VPC。对于 `subnet-id`，请提供应在其中创建此端点的子网。对于 `security-group-id`，请指定要与该端点关联的安全组。（它可能与 Lambda 使用的安全组相同。） | 云架构师、云管理员 | 

### 测试多租户与数据隔离
<a name="test-multi-tenancy-and-data-isolation"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 更新索引与搜索函数的 Python 文件。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html)您可以从 OpenSearch 服务控制台的**概述**选项卡获取 Elasticsearch 终端节点。其格式为 `<AWS-Region>.es.amazonaws.com`。 | 云架构师、应用程序开发人员 | 
| 更新 Lambda 代码。 | 使用 AWS CLI [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html)命令使用您对 Python 文件所做的更改来更新 Lambda 代码：<pre>aws lambda update-function-code \<br />  --function-name index-lambda-function \<br />  --zip-file fileb://index_lambda_package.zip<br /><br />aws lambda update-function-code \<br />  --function-name search-lambda-function \<br />  --zip-file fileb://search_lambda_package.zip</pre> | 云架构师、应用程序开发人员 | 
| 将原始数据上传到 S3 存储桶。 | 使用 AWS CLI [cp](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/cp.html) 命令将 Tenant-1 和 Tenant-2 对象的数据上传到`tenantrawdata`存储桶（指定您为此目的创建的 S3 存储桶的名称）：<pre>aws s3 cp tenant-1-data s3://tenantrawdata<br />aws s3 cp tenant-2-data s3://tenantrawdata</pre>S3 存储桶设置为每当上传数据时运行 Lambda 索引函数，便在 Elasticsearch 中为文档编制索引。 | 云架构师、云管理员 | 
| 在 Kibana 控制台搜索数据。 | 在 Kibana 控制台上，运行以下查询：<pre>GET tenant-data/_search</pre>此查询显示了 Elasticsearch 中编制索引的所有文档。在这种情况下，您应该会看到两个单独 Tenant-1 和 Tenant-2 文档。 | 云架构师、云管理员 | 
| 从 API Gateway 中测试搜索 API。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service.html)有关屏幕插图，请参阅[其他信息](#build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-additional)部分。 | 云架构师、应用程序开发人员 | 
| 清理资源。 | 清理您创建的所有资源，以防止对您的账户收取额外费用。 | AWS DevOps、云架构师、云管理员 | 

## 相关资源
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-resources"></a>
+ [AWS SDK for Python (Boto)](https://aws.amazon.com/sdk-for-python/)
+ [AWS Lambda 文档](https://docs.aws.amazon.com/lambda/)
+ [API Gateway 文档](https://docs.aws.amazon.com/apigateway/)
+ [Amazon S3 文档](https://docs.aws.amazon.com/s3/)
+ [亚马逊 OpenSearch 服务文档](https://docs.aws.amazon.com/elasticsearch-service/)
  + [Amazon 服务中的精细访问控制 OpenSearch ](https://docs.amazonaws.cn/en_us/elasticsearch-service/latest/developerguide/fgac.html)
  + [使用 Amazon OpenSearch 服务创建搜索应用程序](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/search-example.html)
  + [在 VPC 内启动您的亚马逊 OpenSearch 服务域](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html)

## 附加信息
<a name="build-a-multi-tenant-serverless-architecture-in-amazon-opensearch-service-additional"></a>

**数据分区模型**

多租户系统中常用的数据分区模型包含三种：孤岛、池和混合。您选择的模型取决于环境合规性、噪音邻居、操作和隔离需求。

*孤岛模型*

在孤岛模型中，每个租户数据都存储在不同的存储区域中，租户数据不会混合。您可以使用两种方法通过 Serv OpenSearch ice 实现孤岛模型：每个租户的域和每个租户的索引。
+ **每个租户的域名** — 您可以为每个租户使用单独的 OpenSearch 服务域（与 Elasticsearch 集群同义）。将每个租户置于自己的域内，可以获得将数据置于独立构造中的所有好处。但是这种方法带来了管理和敏捷性方面的挑战。它的分布性质使得汇总和评测租户的运营状况和活动变得更困难。这是一个成本高昂的选项，要求每个 OpenSearch 服务域至少有三个主节点和两个数据节点来处理生产工作负载。

![\[多租户无服务器架构每租户域孤岛模型。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/c2195f82-e5ed-40bb-b76a-3b0210bf1254.png)


 
+ **每个租户的索引**-您可以将租户数据放在 OpenSearch 服务集群内的单独索引中。使用这种方法，您可在创建索引和命名索引时使用租户标识符，方法是在索引名称之前添加租户标识符。按租户编制索引的方法可帮助实现孤岛目标，而无需为每个租户引入完全独立的集群。但是，如果索引数量增加，您可能会遇到内存压力，因为这种方法需要更多分片，而主节点必须处理更多的分配和重新平衡。

![\[多租户无服务器架构中的每租户索引孤岛模型。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/354a9463-25bb-422b-84de-d4875a7c8ea2.png)


 

**孤岛模型中的隔离** — 在孤岛模型中，您可使用 IAM 策略来隔离存放每个租户数据的域或索引。这些策略阻止一个租户访问另外一个租户的数据。要实现您的孤岛隔离模型，您可创建基于资源的策略来控制对租户资源的访问权限。这通常是一个域访问策略，用于指定委托人可以对域的子资源（包括 Elasticsearch 索引和）执行哪些操作。 APIs借助基于 IAM 身份的策略，您可以指定在域、索引或服务中*允许*或* APIs 拒绝*的操作。 OpenSearch IAM 策略的 `Action` 元素描述该策略允许或拒绝的特定操作，并且 `Principal ` 元素指定受影响的账户、用户或角色。

以下示例策略仅授予 Tenant-1 对 `tenant-1` 域中子资源的完全访问权限（如所指定 `es:*`）。`Resource` 元素中的尾随 `/*` 指示此策略适用于域的子资源，而不是域本身。此策略生效后，不允许租户在现有域创建新域或修改设置。

```
{
   "Version": "2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::<aws-account-id>:user/Tenant-1"
         },
         "Action": "es:*",
         "Resource": "arn:aws:es:<Region>:<account-id>:domain/tenant-1/*"
      }
   ]
}
```

若要实现每个索引孤岛模型的租户，您需要修改此示例策略，通过指定索引名称，进一步将 Tenant-1 限制在指定的索引或索引范围内。以下示例策略将 Tenant-1 限制为 `tenant-index-1` 索引。 

```
{
   "Version": "2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::123456789012:user/Tenant-1"
         },
         "Action": "es:*",
         "Resource": "arn:aws:es:<Region>:<account-id>:domain/test-domain/tenant-index-1/*"
      }
   ]
}
```

*池模型*

在池模型中，所有租户数据都存储至同一域内的索引中。租户标识符包含在数据（文档）中并用作分区键，因此您可确定哪些数据属于哪个租户。此模式减少了管理开销。操作和管理池索引比管理多个索引更加容易、更高效。但是，由于租户数据混合在同一个索引，因此您将失去孤岛模型提供的自然租户隔离。这种方法还可能会因为邻居噪音效应而降低性能。

![\[适用于多租户无服务器架构的池模型。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/c2c3bb0f-6ccd-47a7-ab67-e7f3f8c7f289.png)


 

**池模型中的租户隔离** — 通常，租户隔离难以在池模型中实现。与孤岛模型一起使用的 IAM 机制不允许您根据存储在文档内的租户 ID 来描述隔离。

另一种方法是使用开放发行版为 Elasticsearch 提供的[精细访问控制](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/fgac.html) (FGAC) 支持。FGAC 允许您控制索引、文档或字段级别权限。对于每个请求，FGAC 都会评估用户凭证，然后对用户执行身份验证或拒绝访问。如果 FGAC 对用户进行身份验证，它将获取映射到该用户的所有角色，并使用完整的权限集来确定如何处理请求。 

要在池化模型中实现所需隔离，您可使用[文档级安全性](https://opendistro.github.io/for-elasticsearch-docs/docs/security/access-control/document-level-security/)，这样可以将角色限制为索引中文档的子集。以下示例角色将查询限制为 Tenant-1。通过将此角色应用于 Tenant-1，您可实现必要的隔离。 

```
{
   "bool": {
     "must": {
       "match": {
         "tenantId": "Tenant-1"
       }
     }
   }
 }
```

*混合模型*

混合模型在同一环境中使用孤岛和池模型的组合，为每个租户级别（例如免费、标准和高级等级）提供独特的体验。每层均遵循池模型中使用的相同安全配置文件。

 

![\[适用于多租户无服务器架构混合模型。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/e7def98a-38ef-435a-9881-7e95ae4d4940.png)


**混合模型中的租户隔离** — 在混合模型中，您应遵循与池模型相同的安全配置文件，其中在文档级别使用 FGAC 安全模型可提供租户隔离。尽管此策略简化了集群管理并提供了敏捷性，但它使架构的其他方面变得复杂。例如，您的代码需要额外的复杂性来确定哪个模型与每个租户关联。您还必须确保单租户查询不会饱和整个域并降低其他租户的体验。 

**在 API Gateway 中测试**

*测试 Tenant-1 查询窗口*

![\[测试 Tenant-1 查询窗口。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/a6757d3f-977a-4ecc-90cb-83ab7f1c3588.png)


*测试 Tenant-2 查询窗口*

 

![\[测试 Tenant-2 查询窗口。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/750196bb-03f6-4b6e-92cd-eb7141602547/images/31bfd656-33ca-4750-b6e6-da4d703c2071.png)


## 附件
<a name="attachments-750196bb-03f6-4b6e-92cd-eb7141602547"></a>

要访问与此文档相关联的其他内容，请解压以下文件：[attachment.zip](samples/p-attach/750196bb-03f6-4b6e-92cd-eb7141602547/attachments/attachment.zip)

# 使用 AWS CDK 部署多堆栈应用程序 TypeScript
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript"></a>

*Rahul Sharad Gaikwad 博士，Amazon Web Services*

## Summary
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-summary"></a>

此模式提供了 step-by-step一种使用 AWS Cloud Development Kit (AWS CDK) 在亚马逊网络服务 (AWS) 上部署应用程序的方法。 TypeScript例如，该模式部署无服务器实时分析应用程序。

此模式可构建和部署嵌套堆栈应用程序。父 AWS CloudFormation 堆栈调用子堆栈或嵌套堆栈。 每个子堆栈都构建和部署堆 CloudFormation 栈中定义的 AWS 资源。AWS CDK Toolkit，即命令行界面 (CLI) 命令`cdk`，是 CloudFormation 堆栈的主要接口。

## 先决条件和限制
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-prereqs"></a>

**先决条件**
+ 一个有效的 Amazon Web Services account
+ 现有虚拟私有云（VPC）和子网
+ AWS CDK Toolkit 已安装并配置
+ 具有管理员权限且配备一组访问密钥的用户。
+ Node.js
+ AWS 命令行界面（AWS CLI）

**限制**
+ 由于 AWS CDK 使用 AWS CloudFormation，因此 AWS CDK 应用程序受 CloudFormation 服务配额的限制。有关更多信息，请参阅 [AWS CloudFormation 配额](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html)。

**产品版本**

此模式已使用以下工具和版本构建和测试。
+ AWS CDK Toolkit 1.83.0
+ Node.js 14.13.0
+ npm 7.0.14

此模式应该适用于任何版本的 AWS CDK 或 npm。请注意，13.0.0 至 13.6.0 版本的 Node.js 与 AWS CDK 不兼容。

## 架构
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-architecture"></a>

**目标技术堆栈**
+ AWS Amplify Console
+ Amazon API Gateway
+ AWS CDK
+ Amazon CloudFront
+ Amazon Cognito
+ Amazon DynamoDB
+ Amazon Data Firehose
+ Amazon Kinesis Data Streams
+ AWS Lambda
+ Amazon Simple Storage Service(Amazon S3)

**目标架构**

下图显示了使用带有 AWS CDK 的多堆栈应用程序部署。 TypeScript

![\[VPC 中的堆栈架构，具有一个父堆栈和两个包含资源的子堆栈。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/0ac29a11-1362-4084-92ed-6b85205763ca/images/8f92e86a-aa3d-4f8a-9b11-b92c52a7226c.png)


 

下图显示了示例无服务器实时应用程序架构。

![\[该区域的应用程序架构。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/0ac29a11-1362-4084-92ed-6b85205763ca/images/2df00faf-f871-4aec-9655-19ba2eb14cf8.png)


 

## 工具
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-tools"></a>

**工具**
+ [AWS Amplify Console](https://docs.aws.amazon.com/amplify/latest/userguide/welcome.html) 是在 AWS 中部署全栈网络和移动应用程序的控制中心。Amplify Console hosting 提供了基于 Git 的工作流程，用于托管持续部署的全栈无服务器 Web 应用程序。管理用户界面是一个为前端 Web 和移动开发人员提供的可视化界面，使其可以在 Amazon Web Services Console 外部创建和管理应用程序后端。
+ A@@ [mazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) 是一项 AWS 服务，用于创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) 是一个软件开发框架，可帮助您在代码中定义和预调配 Amazon Web Services Cloud 基础设施。
+ [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) 是一个命令行云开发套件，可帮助您与 AWS CDK 应用程序进行交互。`cdk`CLI 命令是与 AWS CDK 应用程序交互的主要工具。它运行您的应用程序，查询您定义的应用程序模型，并生成和部署由 AWS CDK 生成的 AWS CloudFormation 模板。
+ [亚马逊 CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html)是一项网络服务，可加快静态和动态网页内容（例如.html、.css、.js 和图像文件）的分发。 CloudFront 通过名为边缘位置的全球数据中心网络提供内容，以降低延迟并提高性能。
+ [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) 为您的 Web 和移动应用程序提供身份验证、授权和用户管理。您的用户可以直接登录，也可通过第三方登录。
+ [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 是一种全托管 NoSQL 数据库服务，提供快速而可预测的性能，能够实现无缝扩展。
+ [Amazon Data Firehose 是一项完全托管](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html)的服务，用于向亚马逊 S3、亚马逊 Redshift、 OpenSearch 亚马逊服务、Splunk 等目的地以及受支持的第三方服务提供商拥有的任何自定义 HTTP 终端节点或 HTTP 终端节点提供实时[流式传输数据](https://aws.amazon.com/streaming-data/)。
+ [Amazon Kinesis Data Streams](https://docs.aws.amazon.com/streams/latest/dev/introduction.html) 是一项实时收集和处理大型数据记录流的服务。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，支持无需预置或管理服务器即可运行代码。只有在需要时 Lambda 才运行您的代码，并且能自动扩缩，从每天几个请求扩展到每秒数千个请求。您只需为消耗的计算时间付费 - 代码未运行时不产生费用。
+ [Amazon Simple Storage Service（Amazon S3）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。

**代码**

此模式代码已随附。

## 操作说明
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-epics"></a>

### 安装 AWS CDK Toolkit
<a name="install-aws-cdk-toolkit"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 安装 AWS CDK Toolkit。 | 若要在全局安装 AWS CDK Toolkit，请运行以下命令。`npm install -g aws-cdk` | DevOps | 
| 验证 版本。 | 若要验证 AWS CDK Toolkit 的版本，请运行以下命令。 `cdk --version` | DevOps | 

### 设置 AWS 凭证
<a name="set-up-aws-credentials"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 设置凭证。 | 若要设置凭证，请运行 `aws configure` 命令并按照提示进行操作。<pre>$aws configure<br />AWS Access Key ID [None]: <br />AWS Secret Access Key [None]: your_secret_access_key<br />Default region name [None]:<br />Default output format [None]:</pre> | DevOps | 

### 下载项目代码
<a name="download-the-project-code"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 下载随附的项目代码。 | 有关目录和文件结构的更多信息，请参阅*其他信息*部分。 | DevOps | 

### 引导 AWS CDK 环境
<a name="bootstrap-the-aws-cdk-environment"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 引导 环境。 | 要将 AWS CloudFormation 模板部署到您要使用的账户和 AWS 区域，请运行以下命令。`cdk bootstrap <account>/<Region>`有关更多信息，请参阅 [AWS 文档](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)。 | DevOps | 

### 构建并部署项目
<a name="build-and-deploy-the-project"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 构建 项目。 | 若要构建项目代码，请运行 `npm run build` 命令。 | DevOps | 
| 部署项目。 | 若要部署项目代码，请运行 `cdk deploy` 命令。 |  | 

### 验证输出
<a name="verify-outputs"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 验证堆栈创建。 | 在 AWS 管理控制台上，选择**CloudFormation**。在项目的堆栈中，验证是否已创建了一个父堆栈和两个子堆栈。 | DevOps | 

### 测试应用程序
<a name="test-the-application"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 将数据发送至 Kinesis 数据流。 | 配置您的 Amazon Web Services account，以使用 Amazon Kinesis Data Generator (KDG) 将数据发送至 Kinesis Data Streams。有关更多信息，请参阅 [Amazon Kinesis Data Generator](https://awslabs.github.io/amazon-kinesis-data-generator/web/help.html)。 | DevOps | 
| 创建 Amazon Cognito 用户。 | [要创建 Amazon Cognito 用户，请从 Kinesis 数据生成器帮助页面的 “创建*亚马逊 Cognito 用户” 部分下载 cognito-setup.j CloudFormation son* 模板。](https://awslabs.github.io/amazon-kinesis-data-generator/web/help.html)初始化模板，然后输入 Amazon Cognito **用户名**和**密码**。**输出**选项卡列出了 Kinesis Data Generator URL。 | DevOps | 
| 登录至 Kinesis Data Generator | 若要登录 KDG，请使用您提供的 Amazon Cognito 凭证和 Kinesis Data Generator URL。 | DevOps | 
| 测试应用程序。 | 在 KDG 的**记录模板**、**模板 1** 中，粘贴*其他信息*部分中的测试代码，然后选择**发送数据**。 | DevOps | 
| 测试 API 网关。 | 提取数据后，使用 `GET` 方法检索数据，以测试 API 网关。 | DevOps | 

## 相关资源
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-resources"></a>

**参考**
+ [AWS Cloud Development Kit](https://aws.amazon.com/cdk/)
+ [AWS CDK 开启 GitHub](https://github.com/aws/aws-cdk)
+ [使用嵌套堆栈](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html)
+ [AWS 示例 - 无服务器实时分析](https://github.com/aws-samples/serverless-realtime-analytics)

## 附加信息
<a name="deploy-multiple-stack-applications-using-aws-cdk-with-typescript-additional"></a>

**目录与文件详细信息**

此模式设置了以下三个堆栈。
+ `parent-cdk-stack.ts` – 此堆栈充当父堆栈，并将两个子应用程序调用为嵌套堆栈。 
+ `real-time-analytics-poc-stack.ts` – 此嵌套堆栈包含基础设施和应用程序代码。
+ `real-time-analytics-web-stack.ts` – 此嵌套堆栈仅包含静态 Web 应用程序代码。

*重要文件及其功能*
+ `bin/real-time-analytics-poc.ts` – AWS CDK 应用程序的接入点。其可加载 `lib/` 定义的所有堆栈。
+ `lib/real-time-analytics-poc-stack.ts` – AWS CDK 应用程序堆栈的定义 (`real-time-analytics-poc`)。
+ `lib/real-time-analytics-web-stack.ts` – AWS CDK 应用程序堆栈的定义 (`real-time-analytics-web-stack`)。
+ `lib/parent-cdk-stack.ts` – AWS CDK 应用程序堆栈的定义 (`parent-cdk`)。
+ `package.json`— npm 模块清单，其中包含应用程序名称、版本和依赖项。
+ `package-lock.json` – 由 npm 维护。
+ `cdk.json` – 用于运行应用程序的工具包。
+ `tsconfig.json`— 项目的 TypeScript 配置。
+ `.gitignore` – Git 应从源代码中排除的文件列表。
+ `node_modules` – 由 npm 维护；包括项目的依赖项。

父堆栈中的以下代码部分将子应用程序调用为嵌套 AWS CDK 堆栈。

```
import * as cdk from '@aws-cdk/core';
import { Construct, Stack, StackProps } from '@aws-cdk/core';
import { RealTimeAnalyticsPocStack } from './real-time-analytics-poc-stack';
import { RealTimeAnalyticsWebStack } from './real-time-analytics-web-stack';


export class CdkParentStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);


    new RealTimeAnalyticsPocStack(this, 'RealTimeAnalyticsPocStack');
    new RealTimeAnalyticsWebStack(this, 'RealTimeAnalyticsWebStack');
  }
}
```

**测试代码**

```
session={{date.now('YYYYMMDD')}}|sequence={{date.now('x')}}|reception={{date.now('x')}}|instrument={{random.number(9)}}|l={{random.number(20)}}|price_0={{random.number({"min":10000, "max":30000})}}|price_1={{random.number({"min":10000, "max":30000})}}|price_2={{random.number({"min":10000, "max":30000})}}|price_3={{random.number({"min":10000, "max":30000})}}|price_4={{random.number({"min":10000, "max":30000})}}|price_5={{random.number({"min":10000, "max":30000})}}|price_6={{random.number({"min":10000, "max":30000})}}|price_7={{random.number({"min":10000, "max":30000})}}|price_8={{random.number({"min":10000, "max":30000})}}|
```

**正在测试 API 网关**

在 API Gateway 控制台上，使用 `GET` 方法测试 API Gateway。

![\[API Gateway 控制台，并已选择“选项”下的 GET。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/0ac29a11-1362-4084-92ed-6b85205763ca/images/452e5b8f-6d61-401d-8484-e5a436cb6f1b.png)


 

## 附件
<a name="attachments-0ac29a11-1362-4084-92ed-6b85205763ca"></a>

要访问与此文档相关联的其他内容，请解压以下文件：[attachment.zip](samples/p-attach/0ac29a11-1362-4084-92ed-6b85205763ca/attachments/attachment.zip)

# 使用 AWS SAM 自动部署嵌套应用程序
<a name="automate-deployment-of-nested-applications-using-aws-sam"></a>

*Rahul Sharad Gaikwad 博士、Ishwar Chauthaiwale、Dmitry Gulin 和 Tabby Ward，Amazon Web Services*

## Summary
<a name="automate-deployment-of-nested-applications-using-aws-sam-summary"></a>

在亚马逊网络服务 (AWS) 上，AWS 无服务器应用程序模型 (AWS SAM) Model 是一个开源框架，它提供用于表达函数 APIs、数据库和事件源映射的速记语法。每个资源只需几行，您就可以定义所需应用程序并使用 YAML 对其进行建模。在部署过程中，SAM 将 SAM 语法转换并扩展为 AWS CloudFormation 语法，您可以使用该语法更快地构建无服务器应用程序。

AWS SAM 简化了 AWS 平台上的无服务器应用程序的开发、部署和管理。它提供标准化框架、更快的部署、本地测试功能、资源管理、与开发工具的无缝集成以及支持社区。这些功能使其成为了高效构建无服务器应用程序的宝贵工具。

该模式使用 AWS SAM 模板自动部署嵌套应用程序。嵌套应用程序是另一应用程序中的应用程序。父应用程序调用其子应用程序。这些是无服务器架构的松耦合组件。 

使用嵌套应用程序，您可重复使用独立编写和维护但使用 AWS SAM 和 Serverless Application Repository 组成的服务或组件，从而快速构建高度复杂的无服务器架构。嵌套应用程序可帮助您构建更强大的应用程序，避免重复工作，并确保整个团队和组织的一致性和最佳实践。为了演示嵌套应用程序，该模式部署了[示例 AWS 无服务器购物车应用程序](https://github.com/aws-samples/aws-sam-nested-stack-sample)。

## 先决条件和限制
<a name="automate-deployment-of-nested-applications-using-aws-sam-prereqs"></a>

**先决条件**
+ 一个活跃的 AWS 账户
+ 现有的虚拟私有云（VPC）和子网
+ 集成式开发环境，例如 Visual Studio Code（有关更多信息，请参阅 [AWS 上的构建工具](https://aws.amazon.com/getting-started/tools-sdks/#IDE_and_IDE_Toolkits)）
+ 使用 pip install wheel 安装 Python wheel 库（如果尚未安装）

**限制**
+ 无服务器应用程序中可以嵌套的最大应用程序数量为 200。
+ 嵌套应用程序的最大参数数量可以是 60。

**产品版本**
+ 此解决方案基于 AWS SAM 命令行界面 (AWS SAM CLI) 版本 1.21.1 构建，但此架构应适用于更高版本的 AWS SAM CLI。

## 架构
<a name="automate-deployment-of-nested-applications-using-aws-sam-architecture"></a>

**目标技术堆栈 **
+ Amazon API Gateway
+ AWS SAM
+ Amazon Cognito
+ Amazon DynamoDB
+ AWS Lambda
+ Amazon Simple Queue Service（Amazon SQS）队列

**目标架构**

下图显示了用户如何通过呼叫向购物服务发出请求 APIs。用户的请求（包括所有必要信息）将发送给 Amazon API Gateway 和 Amazon Cognito 授权机构，后者负责执行身份验证和授权机制。 APIs

当在 DynamoDB 中添加、删除或更新项目时，事件会被放入 DynamoDB Streams 中，DynamoDB Streams 又会启动 Lambda 函数。为了避免在同步工作流中立即删除旧项目，将消息放到 SQS 队列中，该队列会启动工作函数来删除消息。

![\[从 API Gateway 到 Lambda 函数再到 DynamoDB 和产品服务的 POST 和 PUT 操作。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/218adecc-b5b8-4193-9012-b5d584e2e128/images/5b454bae-5fd4-405d-a37d-6bafc3fcf889.png)


在此解决方案设置中，AWS SAM CLI 充当 AWS CloudFormation 堆栈的接口。AWS SAM 模板自动部署嵌套应用程序。父 SAM 模板调用子模板，父 CloudFormation 堆栈部署子堆栈。每个子堆栈都构建 AWS SAM CloudFormation 模板中定义的 AWS 资源。

![\[使用包含父堆栈和三个子 CloudFormation 堆栈的 AWS SAM CLI 的四步流程。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/218adecc-b5b8-4193-9012-b5d584e2e128/images/5828026e-72ad-4a3f-a5f2-bffac0f13e42.png)


1. 构建并部署堆栈。

1. 身份验证 CloudFormation 堆栈包含 Amazon Cognito。

1. 产品 CloudFormation 堆栈包含 Lambda 函数和 Amazon API Gateway

1. 购物 CloudFormation 堆栈包含 Lambda 函数、亚马逊 API Gateway、SQS 队列和亚马逊 DynamoDB 数据库。

## 工具
<a name="automate-deployment-of-nested-applications-using-aws-sam-tools"></a>

**工具**
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) 可帮助您创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) 可帮助您设置 AWS 资源，快速一致地配置这些资源，并在 AWS 账户和区域的整个生命周期中对其进行管理。
+ [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) 为您的 Web 和移动应用程序提供身份验证、授权和用户管理。
+ [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 是一项完全托管式 NoSQL 数据库服务，可提供快速、可预测、可扩展的性能。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，而无需预置或管理服务器。它仅在需要时运行您的代码，并且能自动扩缩，因此您只需为使用的计算时间付费。
+ [AWS Serverless Application Model (AWS SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) 是一个帮助用于构建 AWS Cloud中无服务器应用程序的开源框架。
+ [Amazon Simple Queue Service (Amazon SQS)](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html) 提供了一个安全、持久且可用的托管队列，它可帮助您集成和分离分布式软件系统与组件。

**代码**

此模式的代码可在 GitHub [AWS SAM 嵌套堆栈示例](https://github.com/aws-samples/aws-sam-nested-stack-sample)存储库中找到。

## 操作说明
<a name="automate-deployment-of-nested-applications-using-aws-sam-epics"></a>

### 安装 AWS SAM CLI
<a name="install-aws-sam-cli"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 安装 AWS SAM CLI。 | 要安装 AWS SAM CLI，请参阅 [AWS SAM 文档](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)中的说明。 | DevOps 工程师 | 
| 设置 AWS 凭证 | 要设置 AWS 凭证以便 AWS SAM CLI 可以代表您调用 Amazon Web Services，请运行 `aws configure` 命令并按照提示进行操作。<pre>$aws configure<br />AWS Access Key ID [None]: <your_access_key_id><br />AWS Secret Access Key [None]: your_secret_access_key<br />Default region name [None]:<br />Default output format [None]:</pre>有关设置凭证的更多信息，请参阅[身份验证和访问凭证](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-authentication.html)。 | DevOps 工程师 | 

### 初始化 AWS SAM 项目
<a name="initialize-the-aws-sam-project"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 克隆 AWS SAM 代码存储库。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/automate-deployment-of-nested-applications-using-aws-sam.html) | DevOps 工程师 | 
| 部署模板，以初始化项目。 | 要初始化项目，请运行 `SAM init` 命令。当系统提示您选择模板来源时，选择 `Custom Template Location`。 | DevOps 工程师 | 

### 编译和构建 SAM 模板代码
<a name="compile-and-build-the-sam-template-code"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 查看 AWS SAM 应用程序模板。 | 查看嵌套应用程序模板。此示例使用以下嵌套应用程序模板：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/automate-deployment-of-nested-applications-using-aws-sam.html) | DevOps 工程师 | 
| 查看父级模板。 | 查看将调用嵌套应用程序模板的模板。在此示例中，父模板是 `template.yml`。所有单独的应用程序都嵌套在单父模板 `template.yml` 中。 | DevOps 工程师 | 
| 编译和构建 AWS SAM 模板代码。 | 使用 AWS SAM CLI，运行以下命令。<pre>sam build</pre> | DevOps 工程师 | 

### 部署 AWS SAM 模板
<a name="deploy-the-aws-sam-template"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 部署应用程序。 | 要启动用于创建嵌套应用程序 CloudFormation 堆栈并在 AWS 环境中部署代码的 SAM 模板代码，请运行以下命令。<pre>sam deploy --guided --stack-name shopping-cart-nested-stack --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND</pre>此命令将提示几个问题。用 `y` 回答所有问题。 | DevOps 工程师 | 

### 验证部署
<a name="verify-the-deployment"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 验证堆栈。 | 要查看在 AWS SAM 模板中定义的 AWS CloudFormation 堆栈和 AWS 资源，请执行以下操作：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/automate-deployment-of-nested-applications-using-aws-sam.html) | DevOps 工程师 | 

## 相关资源
<a name="automate-deployment-of-nested-applications-using-aws-sam-resources"></a>

**参考**
+ [AWS Serverless Application Model (AWS SAM)](https://aws.amazon.com/serverless/sam/#:~:text=The%20AWS%20Serverless%20Application%20Model,and%20model%20it%20using%20YAML.)
+ [AWS SAM 开启 GitHub](https://github.com/aws/serverless-application-model)
+ [无服务器购物车微服务](https://github.com/aws-samples/aws-serverless-shopping-cart)（AWS 示例应用程序）

**教程和视频**
+ [构建无服务器应用程序](https://youtu.be/Hv3YrP8G4ag)
+ [AWS 在线技术讲座：使用 AWS SAM 构建与部署无服务器应用程序](https://youtu.be/1NU7vyJw9LU)

## 附加信息
<a name="automate-deployment-of-nested-applications-using-aws-sam-additional"></a>

所有代码都准备就绪后，该示例包含以下目录结构：
+ [sam\$1stacks](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html) — 此文件夹包含 `shared.py` 层。层是包含库、自定义运行时系统或其他依赖项的文件存档。利用层，您可在函数中使用库，而不必将库包含在部署包中。
+ *product-mock-service*— 此文件夹包含所有与产品相关的 Lambda 函数和文件。
+ *shopping-cart-service*— 此文件夹包含所有与购物相关的 Lambda 函数和文件。

# 使用 AWS Lambda 代币自动售货机对 Amazon S3 实施 SaaS 租户隔离
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine"></a>

*Tabby Ward、Thomas Davis 和 Sravan Periyathambi，Amazon Web Services*

## Summary
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-summary"></a>

多租户 SaaS 应用程序必须实施系统，以确保保持租户隔离。当您将租户数据存储在同一个 AWS 资源上时，例如多个租户将数据存储在同一个亚马逊简单存储服务 (Amazon S3) Service 存储桶中时，您必须确保不会发生跨租户访问。代币自动售货机 (TVMs) 是提供租户数据隔离的一种方式。这些机器提供了一种获取令牌的机制，同时减少了这些令牌生成方式的复杂性。开发人员可以在不详细了解 TVM 如何生成令牌的情况下使用 TVM。

此模式通过使用 AWS Lambda实现 TVM。TVM 生成令牌，该令牌由临时安全令牌服务 (STS) 凭证组成，这些凭证限制对 S3 存储桶中单个 SaaS 租户数据的访问。

TVMs，以及此模式提供的代码，通常与派生自 JSON Web Tokens (JWTs) 的声明一起使用，以将 AWS 资源请求与租户范围 AWS Identity and Access Management (IAM) 策略关联起来。您可以使用此模式中的代码为基础实现 SaaS 应用程序，该应用程序根据 JWT 令牌中提供的语句生成范围内的临时 STS 凭证。

## 先决条件和限制
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-prereqs"></a>

**先决条件**
+ 活跃 AWS 账户的.
+ AWS Command Line Interface (AWS CLI) [版本 1.19.0 或更高版本](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html)，已在 macOS、Linux 或 Windows 上安装和配置。或者，您可以使用 [2.1 或更高 AWS CLI 版本](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)。

**限制**
+ 此代码在 Java 中运行，当前不支持其他编程语言。 
+ 示例应用程序不包括 AWS 跨区域或灾难恢复 (DR) 支持。 
+ 此模式介绍了适用于 SaaS 应用程序的 Lambda TVM 如何提供限定范围的租户访问权限。如果没有额外的安全测试作为特定应用程序或使用案例的一部分，则并未预期在生产环境中使用此模式。

## 架构
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-architecture"></a>

**目标技术堆栈**
+ AWS Lambda
+ Amazon S3
+ IAM
+ AWS Security Token Service (AWS STS)

**目标架构 **

![\[生成令牌以获取临时 STS 凭证，从而访问 S3 存储桶中的数据。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/97a34c8e-d04e-40b6-acbf-1baa176d22a9/images/14d0508a-703b-4229-85e6-c5094de7fe01.png)


 

## 工具
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-tools"></a>

**AWS 服务**
+ [AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) 是一个开源工具，可帮助您 AWS 服务 通过命令行外壳中的命令进行交互。
+ [AWS Identity and Access Management (IAM)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) 通过控制谁经过身份验证并有权使用 AWS 资源，从而帮助您安全地管理对资源的访问权限。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，无需预调配或管理服务器。它只在需要时运行您的代码，并自动进行扩展，因此您只需为使用的计算时间付费。
+ [AWS Security Token Service (AWS STS)](https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html) 可帮助您为用户申请临时的、有限权限的证书。
+ [Amazon Simple Storage Service（Amazon S3）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。

**代码**

此模式的源代码以附件形式提供，包含以下文件：
+ `s3UploadSample.jar` 提供将 JSON 文档上传至 S3 存储桶的 Lambda 函数的源代码。
+ `tvm-layer.zip` 提供了一个可重复使用的 Java 库，该库为 Lambda 函数提供用于访问 S3 存储桶和上传 JSON 文档的令牌（STS 临时凭证）。
+ `token-vending-machine-sample-app.zip` 提供了用于创建这些构件的源代码和编译说明。

要使用这些文件，请按照下一节中的说明操作。

## 操作说明
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-epics"></a>

### 确定变量值
<a name="determine-variable-values"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 确定变量值。 | 此模式的实现包含几个必须一致使用的变量名。确定每个变量应使用的值，并在后续步骤请求时提供该值。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine.html) | 云管理员 | 

### 创建 S3 存储桶
<a name="create-an-s3-bucket"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 为示例应用程序创建一个 S3 存储桶。 | 使用以下 AWS CLI 命令创建 S3 存储桶。在代码片段中提供 `<sample-app-bucket-name>`** **值：<pre>aws s3api create-bucket --bucket <sample-app-bucket-name></pre>Lambda 示例应用程序将 JSON 文件上传至此存储桶。 | 云管理员 | 

### 创建 IAM TVM 角色和策略
<a name="create-the-iam-tvm-role-and-policy"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 TVM 角色。 | 使用以下 AWS CLI 命令之一创建 IAM 角色。在命令中提供 `<sample-tvm-role-name>`** **值。对于 macOS 或 Linux shell：<pre>aws iam create-role \<br />--role-name <sample-tvm-role-name> \<br />--assume-role-policy-document '{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Effect": "Allow",<br />            "Action": [<br />                "sts:AssumeRole"<br />            ],<br />            "Principal": {<br />                "Service": [<br />                    "lambda.amazonaws.com"<br />                ]<br />            },<br />            "Condition": {<br />                "StringEquals": {<br />                    "aws:SourceAccount": "<AWS Account ID>"<br />                }<br />            }<br />        }<br />    ]<br />}'</pre>对于 Windows 命令行：<pre>aws iam create-role ^<br />--role-name <sample-tvm-role-name> ^<br />--assume-role-policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"sts:AssumeRole\"], \"Principal\": {\"Service\": [\"lambda.amazonaws.com\"]}, \"Condition\": {\"StringEquals\": {\"aws:SourceAccount\": \"<AWS Account ID>\"}}}]}"</pre>调用应用程序时，Lambda 示例应用程序将代入此角色。通过范围策略代入应用程序角色的能力，为代码提供了更广泛的权限来访问 S3 存储桶。 | 云管理员 | 
| 创建内联 TVM 角色策略。 | 使用以下 AWS CLI 命令之一创建 IAM 策略。在命令中提供 `<sample-tvm-role-name>`、****`<AWS Account ID>` 和 `<sample-app-role-name>` 值。对于 macOS 或 Linux shell：<pre>aws iam put-role-policy \<br />--role-name <sample-tvm-role-name> \<br />--policy-name assume-app-role \<br />--policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": "Allow", <br />            "Action": "sts:AssumeRole", <br />            "Resource": "arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam put-role-policy ^<br />--role-name <sample-tvm-role-name> ^<br />--policy-name assume-app-role ^<br />--policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": \"sts:AssumeRole\", \"Resource\": \"arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>\"}]}"</pre>此策略附加在 TVM 角色上。它使代码能够代入应用程序角色，该角色具有更广泛的 S3 存储桶访问权限。 | 云管理员 | 
| 附加托管 Lambda 策略。 | 使用以下 AWS CLI 命令附加 `AWSLambdaBasicExecutionRole` IAM 策略。在命令中提供 `<sample-tvm-role-name>` 值：<pre>aws iam attach-role-policy \<br />--role-name <sample-tvm-role-name> \<br />--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole</pre>对于 Windows 命令行：<pre>aws iam attach-role-policy ^<br />--role-name <sample-tvm-role-name> ^<br />--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole</pre>此托管策略附加到 TVM 角色，允许 Lambda 向亚马逊发送日志。 CloudWatch | 云管理员 | 

### 创建 IAM 应用程序角色与策略
<a name="create-the-iam-application-role-and-policy"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建应用程序角色。 | 使用以下 AWS CLI 命令之一创建 IAM 角色。在命令中提供 `<sample-app-role-name>`、`<AWS Account ID>` 和 `<sample-tvm-role-name>` 值。对于 macOS 或 Linux shell：<pre>aws iam create-role \<br />--role-name <sample-app-role-name> \<br />--assume-role-policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": <br />            "Allow",<br />            "Principal": {<br />                "AWS": "arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name>"<br />            },<br />            "Action": "sts:AssumeRole"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam create-role ^<br />--role-name <sample-app-role-name> ^<br />--assume-role-policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\",\"Principal\": {\"AWS\": \"arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name>\"},\"Action\": \"sts:AssumeRole\"}]}"</pre>Lambda 示例应用程序通过限定范围的策略代入此角色，以获得基于租户的 S3 存储桶访问权限。 | 云管理员 | 
| 创建内联应用程序角色策略。 | 使用以下 AWS CLI 命令之一创建 IAM 策略。在命令中提供 `<sample-app-role-name>` 和 `<sample-app-bucket-name>`** **值。对于 macOS 或 Linux shell：<pre>aws iam put-role-policy \<br />--role-name <sample-app-role-name> \<br />--policy-name s3-bucket-access \<br />--policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": "Allow", <br />            "Action": [<br />                "s3:PutObject", <br />                "s3:GetObject", <br />                "s3:DeleteObject"<br />            ], <br />            "Resource": "arn:aws:s3:::<sample-app-bucket-name>/*"<br />        }, <br />        {<br />            "Effect": "Allow", <br />            "Action": ["s3:ListBucket"], <br />            "Resource": "arn:aws:s3:::<sample-app-bucket-name>"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam put-role-policy ^<br />--role-name <sample-app-role-name> ^<br />--policy-name s3-bucket-access ^<br />--policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"s3:PutObject\", \"s3:GetObject\", \"s3:DeleteObject\"], \"Resource\": \"arn:aws:s3:::<sample-app-bucket-name>/*\"}, {\"Effect\": \"Allow\", \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::<sample-app-bucket-name>\"}]}"</pre>此策略附加在应用程序角色上。提供了对 S3 存储桶中对象的广泛访问权限。当示例应用程序代入该角色时，这些权限将限定为使用 TVM 动态生成的策略的特定租户。 | 云管理员 | 

### 使用 TVM 创建 Lambda 示例应用程序
<a name="create-the-lam-sample-application-with-tvm"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 下载编译后源文件。 | 下载 `s3UploadSample.jar` 和 `tvm-layer.zip` ****文件，它们包含在附件内。`token-vending-machine-sample-app.zip` 中提供了用于创建这些构件的源代码和编译说明。 | 云管理员 | 
| 创建 Lambda 层。 | 使用以下 AWS CLI 命令创建 Lambda 层，这样 Lambda 就可以访问 TVM。 如果您不是从下载 ` tvm-layer.zip` 的位置运行此命令，请在 `--zip-file` 参数中提供正确的 `tvm-layer.zip` 路径。 <pre>aws lambda publish-layer-version \<br />--layer-name sample-token-vending-machine \<br />--compatible-runtimes java11 \<br />--zip-file fileb://tvm-layer.zip</pre>对于 Windows 命令行：<pre>aws lambda publish-layer-version ^<br />--layer-name sample-token-vending-machine ^<br />--compatible-runtimes java11 ^<br />--zip-file fileb://tvm-layer.zip</pre>此命令创建 Lambda 层，其中包含可重复使用的 TVM 库。 | 云管理员、应用程序开发人员 | 
| 创建 Lambda 函数。 | 使用以下 AWS CLI 命令创建 Lambda 函数。在命令中提供 `<sample-app-function-name>`、`<AWS Account ID>`、`<AWS Region>`、`<sample-tvm-role-name>`、`<sample-app-bucket-name>` 和 `<sample-app-role-name>` 值。 如果您不是从下载 `s3UploadSample.jar` 的位置运行此命令，请在 `--zip-file` 参数中提供正确的 `s3UploadSample.jar` 路径。 <pre>aws lambda create-function \<br />--function-name <sample-app-function-name>  \<br />--timeout 30 \<br />--memory-size 256 \<br />--runtime java11 \<br />--role arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name> \<br />--handler com.amazon.aws.s3UploadSample.App \<br />--zip-file fileb://s3UploadSample.jar \<br />--layers arn:aws:lambda:<AWS Region>:<AWS Account ID>:layer:sample-token-vending-machine:1 \<br />--environment "Variables={S3_BUCKET=<sample-app-bucket-name>,<br />ROLE=arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>}"</pre>对于 Windows 命令行：<pre>aws lambda create-function ^<br />--function-name <sample-app-function-name>  ^<br />--timeout 30 ^<br />--memory-size 256 ^<br />--runtime java11 ^<br />--role arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name> ^<br />--handler com.amazon.aws.s3UploadSample.App ^<br />--zip-file fileb://s3UploadSample.jar ^<br />--layers arn:aws:lambda:<AWS Region>:<AWS Account ID>:layer:sample-token-vending-machine:1 ^<br />--environment "Variables={S3_BUCKET=<sample-app-bucket-name>,ROLE=arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>}"</pre>此命令创建 Lambda 函数，其中包含示例应用程序代码和附加的 TVM 层。它还设置了两个环境变量：`S3_BUCKET` 和 `ROLE`。示例应用程序使用这些变量来确定要代入的角色以及要将 JSON 文档上传到的 S3 存储桶。 | 云管理员、应用程序开发人员 | 

### 测试示例应用程序和 TVM。
<a name="test-the-sample-application-and-tvm"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 调用 Lambda 示例应用程序。 | 使用以下 AWS CLI 命令之一以预期的有效负载启动 Lambda 示例应用程序。在命令中提供 `<sample-app-function-name>` 和 `<sample-tenant-name>` 值。对于 macOS 和 Linux shell：<pre>aws lambda invoke \<br />--function <sample-app-function-name> \<br />--invocation-type RequestResponse \<br />--payload '{"tenant": "<sample-tenant-name>"}' \<br />--cli-binary-format raw-in-base64-out response.json</pre>对于 Windows 命令行：<pre>aws lambda invoke ^<br />--function <sample-app-function-name> ^<br />--invocation-type RequestResponse ^<br />--payload "{\"tenant\": \"<sample-tenant-name>\"}" ^<br />--cli-binary-format raw-in-base64-out response.json</pre>此命令调用 Lambda 函数并在 `response.json` 文档中返回结果。在许多基于 Unix 的系统上，您可以将 `response.json` 更改为 `/dev/stdout`，将结果直接输出到 Shell，而无需创建其他文件。 在后续调用此 Lambda 函数时更改 `<sample-tenant-name>` 值，会改变 JSON 文档的位置和令牌提供的权限。 | 云管理员、应用程序开发人员 | 
| 查看 S3 存储桶，以查看创建的对象。 | 浏览到您之前创建的 S3 存储桶（`<sample-app-bucket-name>`）。此存储桶包含 S3 对象前缀，其值为 `<sample-tenant-name>`。在此前缀下，您将找到以 UUID 命名的 JSON 文档。多次调用示例应用程序将添加更多 JSON 文档。 | 云管理员 | 
| 在日志中查看示例应用程序的 CloudWatch 日志。 | 查看与日志中名为 Lambda 函数关联的日志`<sample-app-function-name>`。 CloudWatch 有关说明，请参阅 [Lambda 文档中的将 Lambda 函数 CloudWatch 日志发送到日志](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html)。您可在这些日志中查看 TVM 生成的租户范围策略。此租户范围策略向 Amazon S3 **PutObject**、、、和授予使用示例应用程序的权限 **GetObject**DeleteObject**ListBucket****** APIs，但仅限于与之关联的对象前缀。`<sample-tenant-name>`在后续调用示例应用程序时，如果您更改 `<sample-tenant-name>`，TVM 会更新范围内的策略，使其与调用有效载荷中提供的租户相对应。此动态生成的策略显示了如何在 SaaS 应用程序中使用 TVM 维护租户范围访问权限。 TVM 功能在 Lambda 层中提供，因此无需复制代码即可将其附加至应用程序使用的其他 Lambda 函数。有关动态生成的策略的说明，请参阅[其他信息](#implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-additional)部分。 | 云管理员 | 

## 相关资源
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-resources"></a>
+ [使用动态生成的 IAM policy 隔离租户](https://aws.amazon.com/blogs/apn/isolating-saas-tenants-with-dynamically-generated-iam-policies/)（博客文章）
+ [在 SaaS 环境中应用动态生成的隔离策略](https://aws.amazon.com/blogs/apn/applying-dynamically-generated-isolation-policies-in-saas-environments/)（博客文章）
+ [SaaS 开启 AWS](https://aws.amazon.com/saas/)

## 附加信息
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-additional"></a>

以下日志显示了在这种模式下由 TVM 代码生成的动态生成的策略。在此屏幕截图中，`<sample-app-bucket-name>` 是 `DOC-EXAMPLE-BUCKET`，`<sample-tenant-name>` 是 `test-tenant-1`。此范围策略返回的 STS 凭证无法对 S3 存储桶中的对象执行任何操作，但与对象密钥前缀 `test-tenant-1` 关联的对象除外。

![\[显示了由 TVM 代码生成的动态生成的策略的日志。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/97a34c8e-d04e-40b6-acbf-1baa176d22a9/images/d4776ebe-fb8f-41ac-b8c5-b4f97a821c8c.png)


## 附件
<a name="attachments-97a34c8e-d04e-40b6-acbf-1baa176d22a9"></a>

要访问与此文档相关联的其他内容，请解压以下文件：[attachment.zip](samples/p-attach/97a34c8e-d04e-40b6-acbf-1baa176d22a9/attachments/attachment.zip)

# 使用 AWS Step Functions 实施无服务器 saga 模式
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions"></a>

*Tabby Ward、Joe Kern 和 Rohan Mehta，Amazon Web Services*

## Summary
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-summary"></a>

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

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

此模式演示了如何使用 AWS Step Functions、AWS Lambda 和 Amazon DynamoDB 等无服务器技术自动设置和部署示例应用程序（用于处理差旅预订）。示例应用程序还使用 Amazon API Gateway 和 Amazon Simple Notiﬁcation Service (Amazon SNS) 实现 saga 执行协调器。该模式可以通过 AWS Cloud Development Kit (AWS CDK)、AWS Serverless Application Model (AWS SAM) 或 Terraform 等基础设施即代码（IaC）框架进行部署。

## 先决条件和限制
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-prereqs"></a>

**先决条件**
+ 一个活跃的 AWS 账户。
+ 创建 AWS CloudFormation 堆栈的权限。有关更多信息，请参阅 CloudFormation 文档中的[控制访问权限](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html)。
+ 使用您的 Amazon Web Services account 配置您选择的 IaC 框架（AWS CDK、AWS SAM 或 Terraform），以便您可使用框架 CLI 部署应用程序。
+ NodeJS，用于在本地构建和运行应用程序。
+ 您选择的代码编辑器（例如 Visual Studio Code、Sublime 或 Atom）。

**产品版本**
+ [NodeJS 版本 14](https://nodejs.org/en/download/)
+ [AWS CDK 版本 2.37.1](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)
+ [AWS SAM 版本 1.71.0](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
+ [Terraform 版本 1.3.7](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)

**限制**

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

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

## 架构
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-architecture"></a>

**目标架构**

拟议架构使用 AWS Step Functions 构建 saga 模式，用于预订航班、预订租车和处理度假付款。

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

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

![\[基于 Saga 模式的旅行预订系统的工作流。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/daad3e8e-6e6b-41c2-95c1-ca79d53ead64.png)


 

**自动化和扩展**

您可以使用其中一个 IaC 框架为此架构创建配置。通过以下链接之一获取首选 IaC。
+ [使用 AWS CDK 部署](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [使用 AWS SAM 部署](https://serverlessland.com/workflows/saga-pattern-sam)
+ [使用 Terraform 部署](https://serverlessland.com/workflows/saga-pattern-tf)

## 工具
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-tools"></a>

**Amazon Web Services**
+ [AWS Step Functions](https://aws.amazon.com/step-functions/) 是一项无服务器编排服务，可让您搭配使用 AWS Lambda 函数和其他 Amazon Web Services 来构建业务关键型应用程序。通过 Step Functions 图形控制台，您可以将应用程序的工作流视为一系列事件驱动的步骤。
+ [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) 是一种全托管 NoSQL 数据库服务，提供快速而可预测的性能，能够实现无缝扩展。您可以使用 DynamoDB 创建一个数据库表来存储和检索任意量级的数据，并支持任何级别的请求流量。
+ [AWS Lambda](https://aws.amazon.com/lambda/) 是一项计算服务，可帮助您运行代码，无需预置或管理服务器。Lambda 只在需要时运行您的代码，并自动进行扩展，从每天几个请求扩展到每秒数千个请求。
+ A@@ [mazon API Gateway](https://aws.amazon.com/api-gateway/) 是一项 AWS 服务，用于创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [Amazon Simple Notiﬁcation Service (Amazon SNS)](https://aws.amazon.com/sns/) 是一项托管服务，提供从发布者至订阅用户的消息传输。
+ [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) 是一个软件开发框架，用于使用熟悉的编程语言（例如，Python TypeScript JavaScript、Java 和 C\$1/Net）来定义您的云应用程序资源。
+ [AWS Serverless Application Model (AWS SAM)](https://aws.amazon.com/serverless/sam/) 是一个用于构建无服务器应用程序的开源框架。它提供了用于表达函数 APIs、数据库和事件源映射的速记语法。

**代码**

可以在以下链接中找到演示 saga 模式的示例应用程序的代码，包括 IaC 模板 (AWS CDK、AWS SAM 或 Terraform)、Lambda 函数和 DynamoDB 表。按照首个操作说明中的说明安装这些工具。
+ [使用 AWS CDK 部署](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [使用 AWS SAM 部署](https://serverlessland.com/workflows/saga-pattern-sam)
+ [使用 Terraform 部署](https://serverlessland.com/workflows/saga-pattern-tf)

## 操作说明
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-epics"></a>

### 安装软件包、编译与构建
<a name="install-packages-compile-and-build"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 安装 NPM 软件包。 | 创建一个新目录，在终端中导航到该目录，然后在此模式前面的 “*代码*” 部分中克隆您选择的 GitHub 存储库。在包含 `package.json` 文件的根文件夹中，运行以下命令下载并安装所有 Node Package Manager (NPM) 软件包：<pre>npm install</pre> | 开发人员、云架构师 | 
| 编译脚本。 | 在根文件夹中，运行以下命令以指示 TypeScript 转译器创建所有必需 JavaScript 的文件：<pre>npm run build</pre> | 开发人员、云架构师 | 
| 注意更改和重新编译。 | 在根文件夹，在单独的终端窗口中运行以下命令，以监视代码更改，并在检测到更改时编译代码：<pre>npm run watch</pre> | 开发人员、云架构师 | 
| 运行单元测试（仅限 AWS CDK）。 | 如果您使用的是 AWS CDK，请在根文件夹中运行以下命令，以执行 Jest 单元测试：<pre>npm run test</pre> | 开发人员、云架构师 | 

### 将资源部署至目标 Amazon Web Services account
<a name="deploy-resources-to-the-target-aws-account"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 将演示堆栈部署至 AWS。 | 该应用程序与 AWS 区域无关。如果您使用配置文件，则必须在 [AWS 命令行界面（AWS CLI）配置文件](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)中或通过 [AWS CLI 环境变量](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)明确声明区域。在根文件夹中，运行以下命令，以创建部署程序集并将其部署至默认 Amazon Web Services account 和区域。AWS CDK：<pre>cdk bootstrap<br />cdk deploy</pre>AWS SAM：<pre>sam build<br />sam deploy --guided</pre>Terraform：<pre>terraform init<br />terraform apply</pre>此步骤可能需要几分钟时间才能完成。此命令使用为 AWS CLI 配置的默认凭证。记下部署完成后控制台上所示 API Gateway 网址。您将需要这些信息测试 saga 的执行流程。 | 开发人员、云架构师 | 
| 将已部署的堆栈与当前状态比较。 | 在根文件夹中，运行以下命令，将已部署的堆栈与更改源代码后的当前状态比较：AWS CDK：<pre>cdk diff</pre>AWS SAM：<pre>sam deploy</pre>Terraform：<pre>terraform plan</pre> | 开发人员、云架构师 | 

### 测试执行流程
<a name="test-the-execution-flow"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 测试 saga 执行流程。 | 导航至您在部署堆栈时在前面的步骤中记下的 API Gateway 网址。此 URL 将触发状态机启动。有关如何通过传递不同的 URL 参数操纵状态机流程的更多信息，请参阅[其他信息](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional)部分。要查看结果，请登录 AWS 管理控制台 并导航到 Step Functions 控制台。在这里，您可以看到 saga 状态机的每一个步骤。您还可以查看 DynamoDB 表以查看已插入、更新或删除记录。如果您经常刷新屏幕，则可以看到事务状态从 `pending` 变为 `confirmed`。 您可以通过使用您的手机号码更新 `stateMachine.ts` 文件中的代码，以订阅 SNS 主题，以便在预订成功或失败时接收 SMS 消息。有关更多信息，请参阅[其他信息](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional)部分中的 *Amazon SNS*。 | 开发人员、云架构师 | 

### 清理
<a name="clean-up"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 清理资源。 | 若要清理为此应用程序部署的资源，可以使用以下命令之一。AWS CDK：<pre>cdk destroy</pre>AWS SAM：<pre>sam delete</pre>Terraform：<pre>terraform destroy</pre> | 应用程序开发人员、云架构师 | 

## 相关资源
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-resources"></a>

**技术论文**
+ [在 AWS 上实施微服务](https://docs.aws.amazon.com/pdfs/whitepapers/latest/microservices-on-aws/microservices-on-aws.pdf)
+ [无服务器应用程序剖析](https://docs.aws.amazon.com/wellarchitected/latest/serverless-applications-lens/welcome.html)

**Amazon Web Services 文档**
+ [AWS CDK 入门](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
+ [AWS SAM 入门](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html)
+ [AWS Step Functions](https://docs.aws.amazon.com/step-functions/)
+ [Amazon DynamoDB](https://docs.aws.amazon.com/dynamodb/)
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/)
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/)
+ [Amazon SNS](https://docs.aws.amazon.com/sns/)

**教程**
+ [无服务器计算实践研讨会](https://aws.amazon.com/serverless-workshops/)

## 附加信息
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional"></a>

**代码**

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

`saga` Lambda 函数 (`sagaLambda.ts`) 从 API Gateway 网址中的查询参数中获取输入，创建以下 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://\$1api gateway url\$1
+ **预订航班失败** ─ https://\$1api gateway url\$1？ **runType= failFlightsReservation**
+ **确认飞行失败** ─ https://\$1api gateway url\$1？ **runType= failFlightsConfirmation**
+ **预订租车失败** ─ https://\$1api gateway url\$1？ **runType= 预留 failCarRental**
+ **确认租车失败** ─ https://\$1api gateway url\$1？ **runType= 确认 failCarRental**
+ **处理付款失败** ─ https://\$1api gateway url\$1?**runType=failPayment**
+ **传递行程 ID** ─ https://\$1api gateway url\$1?**tripID=**\$1默认情况下，行程 ID 是 AWS 请求 ID\$1

**IaC 模板**

链接的存储库包含 IaC 模板，您可使用这些模板来创建整个示例旅行预订应用程序。
+ [使用 AWS CDK 部署](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [使用 AWS SAM 部署](https://serverlessland.com/workflows/saga-pattern-sam)
+ [使用 Terraform 部署](https://serverlessland.com/workflows/saga-pattern-tf)

**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` a `pending` 的记录以预订租车。
+ **确认租车**：更新 Dynamo CarRentals DB 表中的记录，将其`transaction_status`设置为，`confirmed`以确认租车。
+ **取消租车预订：**从 Dynamo CarRentals DB 表中删除记录，以取消待处理的租车。
+ **处理付款**：在 DynamoDB 付款表中插入付款记录。
+ **取消付款**：从 DynamoDB 付款表删除付款记录。

**Amazon SNS**

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

AWS CDK 片段（在以下代码的第二行中添加电话号码）：

```
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'),
});
```

AWS SAM 片段（用您的有效电话号码替换 `+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，其中 “” ReserveCarRental、“” 和 “ProcessPayment” 后面是 “ConfirmFlight” 和 “” ConfirmCarRental。 通过发送给 SNS 主题订阅者的短信通知客户预订成功。

![\[使用 Saga 模式通过 Step Functions 实施的成功预留的示例。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/f58c894e-7721-4bc7-8f7d-29f23faa5dc1.png)


**预订失败**

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

![\[使用 Saga 模式通过 Step Functions 实施的失败预留的示例。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/7c64d326-be27-42c3-b03f-d677efedb9a7.png)


# 通过使用 AWS CDK 设置 Amazon ECS Anywhere 来管理本地容器应用程序
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk"></a>

*Rahul Sharad Gaikwad 博士，Amazon Web Services*

## Summary
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-summary"></a>

[Amazon ECS Anywhere](https://aws.amazon.com/ecs/anywhere/) 是 Amazon Elastic Container Service (Amazon ECS)的扩展。您可以使用 ECS Anywhere 在本地或客户托管环境中部署本机 Amazon ECS 任务。此功能有助于降低成本，并减少复杂的本地容器编排和操作。您可以使用 ECS Anywhere 在本地和云环境中部署和运行容器应用程序。它使您的团队无需学习多个域和技能组合，也无需自行管理复杂的软件。

此模式演示了使用 AWS Cloud Development Kit ([AWS CDK](https://aws.amazon.com/cdk/))堆栈设置 ECS Anywhere 的步骤。

## 先决条件和限制
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-prereqs"></a>

**先决条件**
+ 一个有效的 Amazon Web Services account。
+ 已安装和配置 AWS 命令行界面（AWS CLI）。（请参阅 AWS CLI 文档中的[安装、更新和卸载 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)。） 
+ AWS CDK Toolkit，已安装并配置。（请参阅 AWS CDK 文档中的 [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html)，并按照说明在全球范围内安装版本 2。）
+ 节点包管理器 (npm)，已为中的 AWS CDK 安装和配置。 TypeScript（请参阅 npm 文档中的[下载和安装 Node.js 和 npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)。）

**限制**
+ 有关限制和注意事项，请参阅 Amazon ECS 文档中的[外部实例 (Amazon ECS Anywhere)](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-anywhere.html#ecs-anywhere-considerations)。

**产品版本**
+ AWS CDK Toolkit 版本 2
+ npm 版本 7.20.3 或更高版本
+ Node.js 版本 16.6.1 或更高版本

## 架构
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-architecture"></a>

**目标技术堆栈**
+ AWS CloudFormation
+ AWS CDK
+ Amazon ECS Anywhere
+ AWS Identity and Access Management（AWS IAM）

**目标架构 **

下图说明了使用 AWS CDK 和 ECS Anywhere 设置的高级系统架构 TypeScript，如该模式所实现的那样。

1. 当您部署 AWS CDK 堆栈时，它会在 AWS 上创建一个 CloudFormation 堆栈。

1. 该 CloudFormation 堆栈预配置 Amazon ECS 集群和相关的 AWS 资源。

1. 要将外部实例注册到 Amazon ECS 集群，您必须在虚拟机 (VM) 上安装 AWS Systems Manager Agent (SSM Agent)代理，并将该 VM 注册为 AWS Systems Manager 托管实例。 

1. 对于您向 Amazon ECS 集群注册的每个外部实例，必须安装 SSM Agent、Amazon ECS 容器代理和 Docker。

1. 当外部实例注册并配置到 Amazon ECS 集群时，它可以在注册为外部实例的虚拟机上运行多个容器。

![\[使用 AWS CDK 将 ECS Anywhere 设置为。 TypeScript\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/3ed63c00-40e7-4831-bb9d-63049c3490aa/images/ff7dc774-830d-4b9f-8262-7314afe7a033.png)


 

**自动化和扩展**

此模式提供的[GitHub 存储库](https://github.com/aws-samples/amazon-ecs-anywhere-cdk-samples/)使用 AWS CDK 作为基础设施即代码 (IaC) 工具来创建该架构的配置。AWS CDK 可帮助您编排资源，并设置 ECS Anywhere。

## 工具
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-tools"></a>
+ [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) 是一个软件开发框架，可帮助您在代码中定义和预置 Amazon Web Services Cloud 基础设施。
+ [AWS 命令行界面（AWS CLI）](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)是一种开源工具，它可帮助您通过命令行 Shell 中的命令与 Amazon Web Services 交互。

**代码**

此模式的源代码可在 GitHub [Amazon ECS Anywhere CDK 示例](https://github.com/aws-samples/amazon-ecs-anywhere-cdk-samples)存储库中找到。若要克隆和使用存储库，请按照下一节中的说明进行操作。

## 操作说明
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-epics"></a>

### 验证 AWS CDK 配置
<a name="verify-aws-cdk-configuration"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 验证 AWS CDK 版本。 | 运行以下命令验证 AWS CDK Toolkit 的版本：<pre>cdk --version</pre>此模式需要 AWS CDK 版本 2。如果您使用 AWS CDK 早期版本，请按照 [AWS CDK 文档](https://docs.aws.amazon.com/cdk/v2/guide/cli.html)中的说明对其进行更新。 | DevOps 工程师 | 
| 设置 AWS 凭证。 | 要设置凭证，请运行 `aws configure` 命令并按照提示进行操作：<pre>$aws configure<br />AWS Access Key ID [None]: <your-access-key-ID><br />AWS Secret Access Key [None]: <your-secret-access-key><br />Default region name [None]: <your-Region-name><br />Default output format [None]:</pre> | DevOps 工程师 | 

### 引导 AWS CDK 环境
<a name="bootstrap-the-aws-cdk-environment"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 克隆 AWS CDK 代码存储库。 | 使用以下命令克隆此模式的 GitHub 代码存储库：<pre>git clone https://github.com/aws-samples/amazon-ecs-anywhere-cdk-samples.git</pre> | DevOps 工程师 | 
| 引导 环境。 | 要将 AWS CloudFormation 模板部署到您要使用的账户和 AWS 区域，请运行以下命令：<pre>cdk bootstrap <account-number>/<Region></pre>有关详情，请参阅 AWS CDK 文档中的[引导](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)。 | DevOps 工程师 | 

### 构建和部署项目
<a name="build-and-deploy-the-project"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 安装软件包依赖关系并编译 TypeScript 文件。 | 通过运行以下命令安装软件包依赖关系并编译 TypeScript 文件：<pre>$cd amazon-ecs-anywhere-cdk-samples<br />$npm install<br />$npm fund </pre>这些命令安装示例存储库内的所有软件包。 如果收到有关缺少软件包的任何错误，请使用以下命令之一：<pre>$npm ci   </pre>—或者—<pre>$npm install -g @aws-cdk/<package_name></pre>有关更多信息，请参阅 npm 文档中的 [npm ci](https://docs.npmjs.com/cli/v7/commands/npm-ci) 和 [npm install](https://docs.npmjs.com/cli/v7/commands/npm-install)。 | DevOps 工程师 | 
| 构建项目。 | 若要生成项目代码，请运行以下命令：<pre>npm run build</pre>有关构建和部署项目的更多信息，请参阅 AWS CDK 文档中的[您的第一个 AWS CDK 应用程序](https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html#:~:text=the%20third%20parameter.-,Synthesize%20an%20AWS%20CloudFormation%20template,-Synthesize%20an%20AWS)。 | DevOps 工程师 | 
| 部署项目。 | 若要部署项目代码，请运行以下命令：<pre>cdk deploy</pre> | DevOps 工程师 | 
| 验证堆栈创建和输出。 | 在 [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/) 上打开 AWS CloudFormation 控制台，****然后选择堆栈。`EcsAnywhereStack` “**输出**” 选项卡显示要在外部 VM 上运行的命令。 | DevOps 工程师 | 

### 设置本地计算机
<a name="set-up-an-on-premises-machine"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 使用 Vagrant 设置您的 VM。 | 出于演示目的，你可以使用 V [HashiCorp agrant](https://www.vagrantup.com/) 来创建虚拟机。Vagrant 是一个开源实用程序，用于构建和维护便携式虚拟软件开发环境。通过从放置 Vagrantfile 的根目录下运行 `vagrant up` 命令来创建 Vagrant VM。有关更多信息，请参阅 [Packer 文档](https://www.vagrantup.com/docs/cli/up)。 | DevOps 工程师 | 
| 将您的虚拟机注册为外部实例。 | 1. 使用 `vagrant ssh` 命令登录 Vagrant 虚拟机。有关更多信息，请参阅 [Packer 文档](https://www.vagrantup.com/docs/cli/ssh)。2. 创建激活码和 ID，您可以使用该激活码和 ID 在 AWS Systems Manager 注册虚拟机并激活外部实例。此命令的输出包括 `ActivationId` 和 `ActivationCode` 值： <pre>aws ssm create-activation --iam-role EcsAnywhereInstanceRole | tee ssm-activation.json</pre>3. 导出激活 ID 和代码值：<pre>export ACTIVATION_ID=<activation-ID><br />export ACTIVATION_CODE=<activation-code></pre>4. 在本地服务器或虚拟机 (VM) 上下载安装脚本：<pre>curl -o "ecs-anywhere-install.sh" "https://amazon-ecs-agent.s3.amazonaws.com/ecs-anywhere-install-latest.sh" && sudo chmod +x ecs-anywhere-install.sh</pre>5. 在本地服务器或虚拟机 (VM) 上下载运行脚本：<pre>sudo ./ecs-anywhere-install.sh \<br />    --cluster test-ecs-anywhere \<br />     --activation-id $ACTIVATION_ID \<br />     --activation-code $ACTIVATION_CODE \<br />    --region <Region></pre>有关设置和注册 VM 的更多信息，请参阅 Amazon ECS 文档中的[将外部实例注册到集群](https://docs.amazonaws.cn/en_us/AmazonECS/latest/developerguide/ecs-anywhere-registration.html)。 | DevOps 工程师 | 
| 验证 ECS Anywhere 和外部虚拟机的状态。 | 要验证您的虚拟盒子是否已连接到 Amazon ECS 控制面板并正在运行，请使用以下命令：<pre>aws ssm describe-instance-information<br />aws ecs list-container-instances --cluster $CLUSTER_NAME</pre> | DevOps 工程师 | 

### 清理
<a name="clean-up"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 清理和删除资源。 | 完成此模式后，应删除您创建的资源，以避免产生任何进一步的费用。若要进行清理，请运行以下命令：<pre>cdk destroy</pre> | DevOps 工程师 | 

## 相关资源
<a name="manage-on-premises-container-applications-by-setting-up-amazon-ecs-anywhere-with-the-aws-cdk-resources"></a>
+ [Amazon ECS Anywhere 文档](https://aws.amazon.com/ecs/anywhere/) 
+ [Amazon ECS Anywhere 演示](https://www.youtube.com/watch?v=-eud6yUXsJM)（视频)
+ [Amazon ECS Anywhere 研讨会示例](https://github.com/aws-samples/aws-ecs-anywhere-workshop-samples)

# 在 AWS 上实现 ASP.NET Web 表单应用程序的现代化
<a name="modernize-asp-net-web-forms-applications-on-aws"></a>

*Vijai Anand Ramalingam 和 Sreelaxmi Pai，Amazon Web Services*

## Summary
<a name="modernize-asp-net-web-forms-applications-on-aws-summary"></a>

此模式描述了通过将传统单体 ASP.NET Web 窗体应用程序移植到 AWS 上的 ASP.NET Core 来实现其现代化的步骤。

将 ASP.NET Web 窗体应用程序移植到 ASP.NET Core 可帮助您利用 Linux 的性能、成本节约和强大的生态系统。不过，这可能需要大量的人工操作。在此模式中，使用分阶段方法逐步对旧应用程序进行现代化改造，然后在 Amazon Web Services Cloud 中容器化。

考虑一个用于购物车的传统单体应用程序。假设它是作为 ASP.NET Web 窗体应用程序创建的，并且由带有代码隐藏 (`aspx.cs`) 文件的 .aspx 页组成。现代化过程包括以下步骤：

1. 使用适当的分解模式将单体分解为微服务。有关更多信息，请参阅 AWS Prescriptive Guidance 网站上的指南[将单体架构分解为微服务](https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-decomposing-monoliths/)。

1. 将传统 ASP.NET Web 窗体 (.NET Framework) 应用程序移植到 .NET 5 或更高版本中的 ASP.NET Core。在此模式中，使用 Porting Assistant for .NET 扫描 ASP.NET Web 窗体应用程序，并确定与 ASP.NET Core 的不兼容。这减少了手动移植的工作量。

1. 使用 React 重新开发 Web 表单用户界面层。此模式不涵盖 UI 重新开发。有关说明，请参阅 React 文档中的[创建新的 React 应用程序](https://reactjs.org/docs/create-a-new-react-app.html)。

1. 将 Web 窗体代码后置文件（业务接口）重新开发为 ASP.NET Core Web API。此模式使用 NDepend 报告来帮助识别所需的文件和依赖关系。

1. 使用适用于.NET 的 Porting Assistant，将旧版应用程序中的 shared/common 项目（例如业务逻辑和数据访问）升级到.NET 5 或更高版本。 

1. 添加 Amazon Web Services 来补充您的应用程序。例如，您可以使用 [Amazon CloudWatch Logs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html) 来监控、存储和访问应用程序的日志，使用 [AWS Systems Manager](https://aws.amazon.com/systems-manager/) 来存储您的应用程序设置。

1. 将现代化的 ASP.NET Core 应用程序容器化。此模式在 Visual Studio 中创建一个面向 Linux 的 Docker 文件，并使用 Docker Desktop 在本地对其进行测试。此步骤假设您的旧版应用程序已在本地或亚马逊弹性计算云 (Amazon EC2) Windows 实例上运行。有关更多信息，请参阅在[亚马逊 EC2 Linux 实例上运行 ASP.NET Core Web API Docker 容器的](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/run-an-asp-net-core-web-api-docker-container-on-an-amazon-ec2-linux-instance.html)模式。

1. 将现代化的 ASP.NET Core 应用程序部署到 Amazon Elastic Container Service (Amazon ECS)。此模式不涵盖部署步骤。有关说明，请参阅 [Amazon ECS 研讨会](https://ecsworkshop.com/)。

**注意**  
此模式不涵盖 UI 开发、数据库现代化或容器部署步骤。

## 先决条件和限制
<a name="modernize-asp-net-web-forms-applications-on-aws-prereqs"></a>

**先决条件**
+ [Visual Studio](https://visualstudio.microsoft.com/downloads/) 或 [Visual Studio Code](https://code.visualstudio.com/download)，已下载并安装。
+ 使用 AWS 管理控制台和 AWS 命令行界面(AWS CLI) 版本 2 访问 Amazon Web Services account。（请参阅[配置 AWS CLI 的说明](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)。）
+ Visual Studio 的 AWS Toolkit（请参阅[设置说明](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/setup.html)）。
+ Docker Desktop，[已下载](https://www.docker.com/products/docker-desktop)并安装。
+ .NET SDK，[已下载](https://download.visualstudio.microsoft.com/download/pr/4263dc3b-dc67-4f11-8d46-cc0ae86a232e/66782bbd04c53651f730b2e30a873f18/dotnet-sdk-5.0.203-win-x64.exe)并安装。
+ NDepend 工具，[已下载](https://www.ndepend.com/download)并安装。要安装 Visual Studio 的 NDepend 扩展程序，请运行`NDepend.VisualStudioExtension.Installer`（[参见说明书](https://www.ndepend.com/docs/getting-started-with-ndepend#Part1)）。可以选择 Visual Studio 2019 或 2022，具体取决于你的要求。 
+ Porting Assistant for .NET，[已下载](https://aws.amazon.com/porting-assistant-dotnet/)并安装。

## 架构
<a name="modernize-asp-net-web-forms-applications-on-aws-architecture"></a>

**实现购物车应用程序的现代化**

下图演示了旧版 ASP.NET 购物车应用程序的现代化过程。

![\[对旧式购物车应用程序进行现代化改造\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/36cda8e6-f2cb-4f1a-b37f-fa3045cc5ba1/images/4367e259-9bb3-4eb6-a54d-1c1e2dece7d4.png)


**目标架构**

下图说明了 AWS 上现代化购物车应用程序的架构。ASP.NET Cor APIs e Web 部署到亚马逊 ECS 集群。日志和配置服务由 Amazon CloudWatch Logs 和 AWS Systems Manager 提供。

![\[AWS 上 ASP.NET Web 表单应用程序的目标架构\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/36cda8e6-f2cb-4f1a-b37f-fa3045cc5ba1/images/ed6d65ec-0dc9-43ab-ac07-1f172e089399.png)


## 工具
<a name="modernize-asp-net-web-forms-applications-on-aws-tools"></a>

**Amazon Web Services**
+ [Amazon ECS](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) - Amazon Elastic Container Service (Amazon ECS) 是一项高度可扩展的快速容器管理服务，可助您轻松运行、停止和管理集群上的容器。您可以在由 AWS Fargate 托管的无服务器基础设施上运行任务和服务。或者，为了更好地控制您的基础架构，您可以在自己管理的 EC2 实例集群上运行任务和服务。
+ [Amazon CloudWatch 日 CloudWatch 志](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html) — Amazon Logs 集中您使用的所有系统、应用程序和 AWS 服务的日志。您可以查看和监控日志，搜索特定的错误代码或模式，根据特定字段过滤日志，或将日志安全存档以备将来分析。
+ [AWS Systems Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html) - AWS Systems Manager 是一项 Amazon Web Services，可用于查看和控制 AWS 上的基础设施。使用 Systems Manager 控制台，您可查看来自多个 Amazon Web Services 的操作数据并在 AWS 资源之间自动执行操作任务。Systems Manager 通过扫描托管实例并报告其检测到的所有策略违规行为（或采取纠正措施）来帮助您维护安全性与合规性。

**工具**
+ [Visual Stud](https://visualstudio.microsoft.com/) io 或 [Visual Studio Code](https://code.visualstudio.com/) — 用于构建.NET 应用程序 APIs、Web 和其他程序的工具。
+ [AWS Toolkit for Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) - Visual Studio 的扩展，可帮助开发、调试和部署使用 Amazon Web Services 的 .NET 应用程序。
+ [Docker Desktop](https://www.docker.com/products/docker-desktop) - 一种简化构建和部署容器化应用程序的工具。
+ [NDepend](https://www.ndepend.com/features/)— 一种用于监视.NET 代码的依赖关系、质量问题和代码更改的分析器。
+ [Porting Assistant for .NET](https://aws.amazon.com/porting-assistant-dotnet/) - 一种分析工具，用于扫描 .NET 代码以识别与 .NET Core 的不兼容之处并估计迁移工作量。

## 操作说明
<a name="modernize-asp-net-web-forms-applications-on-aws-epics"></a>

### 将旧版应用程序移植到 .NET 5 或更高版本
<a name="port-your-legacy-application-to-net-5-or-later-version"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 将 .NET Framework 旧应用程序升级到 .NET 5。 | 您可以使用 Porting Assistant for .NET 将旧版 ASP.NET Web Forms 应用程序转换为 .NET 5 或更高版本。请按照 [Porting Assistant for .NET 文档](https://docs.aws.amazon.com/portingassistant/latest/userguide/porting-assistant-getting-started.html)中的说明进行操作。 | 应用程序开发人员 | 
| 生成 NDepend 报告。 | 当您通过将 ASP.NET Web 窗体应用程序分解为微服务来实现现代化时，您可能不需要旧应用程序中的所有 .cs 文件。您可以使用 NDepend 为任何隐藏代码 (.cs) 的文件生成报告，以获取所有调用者和被调用者。此报告可帮助您仅识别和使用微服务中所需文件。安装后 NDepend （参见 “[先决条件](#modernize-asp-net-web-forms-applications-on-aws-prereqs)” 部分），在 Visual Studio 中打开旧版应用程序的解决方案（.sln 文件），然后按照以下步骤操作：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html)此过程会生成代码隐藏文件的报告，其中列出了所有调用者和被调用者。有关依赖关系图的更多信息，请参阅[NDepend 文档](https://www.ndepend.com/docs/visual-studio-dependency-graph)。 | 应用程序开发人员 | 
| 创建新的 .NET 5 解决方案。 | 要为现代化的 ASP.NET Core 网页创建新的.NET 5（或更高版本）结构，请执行以下操作： APIs[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html)有关创建项目和解决方案的详细信息，请参阅 [Visual Studio 文档](https://docs.microsoft.com/en-us/visualstudio/ide/creating-solutions-and-projects)。在构建解决方案和验证功能时，除了已识别的文件外，您可能会 NDepend 发现要添加到解决方案中的其他几个文件。 | 应用程序开发人员 | 

### 更新您的应用程序代码。
<a name="update-your-application-code"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
|  APIs 使用 ASP.NET 核心实现网页。 | 假设您在旧版单体购物车应用程序中确定的微服务之一为*产品*。你在上一个操作说明中为*产品*创建了一个新的 ASP.NET Core Web API 项目。在此步骤中，您将识别和现代化与*产品*相关的所有 Web 窗体（.aspx 页面）。假设*产品*由四个 Web 表单组成，如前面的[架构](#modernize-asp-net-web-forms-applications-on-aws-architecture)部分所示：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html)您应该分析每个 Web 表单，识别发送到数据库以执行某些逻辑的所有请求，并获取响应。您可以将每个请求实现为 Web API 端点。鉴于其 Web 表单，*产品*可以具有以下可能的端点：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html)如前所述，您还可以重复使用升级到.NET 5 的所有其他项目，包括业务逻辑、数据访问和 shared/common 项目。 | 应用程序开发人员 | 
| 配置 Amazon CloudWatch 日志。 | 您可以使用 [Amazon CloudWatch Logs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html) 来监控、存储和访问应用程序的日志。您可以使用 AWS 软件开发工具包将数据记录到 Amazon CloudWatch 日志中。您还可以使用流行的.NET CloudWatch 日志框架（例如 [Log4](https://www.nuget.org/packages/AWS.Logger.Log4net/) Net 和 ASP.NET C [ore 日志框架）将.NET](https://www.nuget.org/packages/AWS.Logger.AspNetCore/) 应用程序与日志集成。[NLog](https://www.nuget.org/packages/AWS.Logger.NLog/)有关此步骤的更多信息，请参阅博客文章 [Amazon CloudWatch 日志和.NET 日志框架](https://aws.amazon.com/blogs/developer/amazon-cloudwatch-logs-and-net-logging-frameworks/)。 | 应用程序开发人员 | 
| 配置 AWS Systems Manager Parameter Store。 | 您可以使用 [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) 来存储应用程序设置，例如与应用程序代码分开的连接字符串。[Amazon.extens.Configurat NuGet SystemsManager](https://www.nuget.org/packages/Amazon.Extensions.Configuration.SystemsManager/)简化了应用程序将这些设置从 AWS Systems Manager Parameter Store 加载到.NET 核心配置系统的方式。 有关此步骤的更多信息，请参阅博客文章 [AWS Systems Manager 的 .NET Core 配置提供程序](https://aws.amazon.com/blogs/developer/net-core-configuration-provider-for-aws-systems-manager/)。 | 应用程序开发人员 | 

### 添加身份验证和授权
<a name="add-authentication-and-authorization"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 使用共享 cookie 进行身份验证。 | 对旧版单体应用程序进行现代化改造是一个迭代过程，需要单体应用及其现代化版本共存。您可以使用共享 cookie 来实现两个版本之间的无缝身份验证。旧版 ASP.NET 应用程序继续验证用户凭据并颁发 cookie，而现代化的 ASP.NET Core 应用程序则验证 cookie。 有关说明和示例代码，请参阅[示例 GitHub 项目](https://github.com/aws-samples/dotnet-share-auth-cookie-between-monolith-and-modernized-apps)。 | 应用程序开发人员 | 

### 在本地生成并运行容器
<a name="build-and-run-the-container-locally"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 使用 Visual Studio 创建 Docker 映像。 | 在此步骤中，将使用 Visual Studio for .NET Core Web API 创建 Docker 文件。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html)Visual Studio 为您的项目创建一个 Docker 文件。有关示例 Docker 文件，请参阅 Microsoft 网站上的 [Visual Studio Container Tools for Docker](https://docs.microsoft.com/en-us/visualstudio/containers/overview)。 | 应用程序开发人员 | 
| 使用 Docker Desktop 构建并运行容器。 | 现在您可以在 Docker Desktop 中构建、创建和运行容器。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/modernize-asp-net-web-forms-applications-on-aws.html) | 应用程序开发人员 | 

## 相关资源
<a name="modernize-asp-net-web-forms-applications-on-aws-resources"></a>
+ [在亚马逊 EC2 Linux 实例上运行 ASP.NET Core Web API Docker 容器（AWS Prescriptive Gu](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/run-an-asp-net-core-web-api-docker-container-on-an-amazon-ec2-linux-instance.html) idence）
+ [Amazon ECS 研讨会](https://ecsworkshop.com/)
+ [ CodeDeploy 使用 AWS 执行 ECS blue/green 部署 CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/blue-green.html)（AWS CloudFormation 文档）
+ [入门 NDepend](https://www.ndepend.com/docs/getting-started-with-ndepend)（NDepend 文档）
+ [Porting Assistant for .NET](https://aws.amazon.com/porting-assistant-dotnet/)

## 附加信息
<a name="modernize-asp-net-web-forms-applications-on-aws-additional"></a>

下表提供了旧版购物车应用程序的示例项目示例，以及新式 ASP.NET Core 应用程序中的等效项目。

**传统解决方案：**


| 
| 
| 项目名称 | 项目模板 | 目标架构 | 
| --- |--- |--- |
| 业务界面  | 类库  | NET Framework。 | 
| BusinessLogic  | 类库  | NET Framework。 | 
| WebApplication  | ASP.NET Framework Web 应用程序  | NET Framework。 | 
| UnitTests  | NUnit 测试项目  | NET Framework。 | 
| 共享->通用  | 类库  | NET Framework。 | 
| 共享->框架  | 类库  | NET Framework。 | 

**新解决方案：**


| 
| 
| 项目名称 | 项目模板 | 目标架构 | 
| --- |--- |--- |
| BusinessLogic  | 类库  | .NET 5.0  | 
| <WebAPI>  | ASP.NET 核心 Web API  | .NET 5.0  | 
| <WebAPI>。 UnitTests | NUnit 3 测试项目  | .NET 5.0  | 
| 共享->通用  | 类库  | .NET 5.0  | 
| 共享->框架  | 类库  | .NET 5.0  | 

# 使用 C\$1 和 AWS CDK 在 SaaS 架构中为孤岛模型进行租户登录
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk"></a>

*Tabby Ward、Susmitha Reddy Gankidi 和 Vijai Anand Ramalingam，Amazon Web Services*

## Summary
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-summary"></a>

软件即服务（SaaS）应用程序可以使用各种不同的架构模型构建。*孤岛模型*是指向租户提供专用资源的架构。

SaaS 应用程序依靠无摩擦模型将新租户引入其环境。这通常需要编排多个组件，才能成功预调配和配置创建新租户所需所有元素。在 SaaS 架构中，此过程称为租户登录。在每个 SaaS 环境中，应通过在登录流程中使用基础设施即代码，实现登录完全自动化。

此模式将引导您完成在 Amazon Web Services（AWS）上创建租户并为租户预调配基本基础设施的示例。该模式使用 C\$1 和 AWS Cloud Development Kit（AWS CDK）。

因为这种模式会产生账单警报，所以我们建议在美国东部（弗吉尼亚州北部）或 us-east-1 AWS 区域 部署堆栈。有关更多信息，请参阅 [AWS 文档](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html)。

## 先决条件和限制
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-prereqs"></a>

**先决条件******
+ 一个活跃的 [AWS 账户](https://aws.amazon.com/account/)。
+ 要在这种模式下创建 AWS 资源，需要具有足够的 IAM 访问权限的 AWS Identity and Access Management（IAM）主体。有关更多信息，请参阅 [IAM 角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)。
+ [安装 Amazon 命令行界面（AWS CLI）](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)并[配置 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) 以执行 AWS CDK 部署。
+ 已下载并安装 [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) 或者已下载并安装 [Visual Studio Code](https://code.visualstudio.com/download)。
+ 已设置 [AWS Toolkit for Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/setup.html)。
+ [.NET Core 3.1 或更高版本](https://dotnet.microsoft.com/download/dotnet-core/3.1)（C\$1 AWS CDK 应用程序需要此选项）
+ 已安装 [Amazon.Lambda.Tools](https://github.com/aws/aws-extensions-for-dotnet-cli#aws-lambda-amazonlambdatools)。

**限制**** **
+ AWS CDK 使用 [AWS CloudFormation](https://aws.amazon.com/cloudformation/)，因此 AWS CDK 应用程序受 CloudFormation 服务配额的限制。有关更多信息，请参阅 [AWS CloudFormation 配额](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html)。 
+ 租户 CloudFormation 堆栈是使用 CloudFormation 服务角色创建的，操作上`infra-cloudformation-role`带有通配符（`sns`\$1 和`sqs*`），但资源锁定到`tenant-cluster`前缀。对于生产使用案例，请评估此设置并仅提供对该服务角色所需访问权限。`InfrastructureProvision`Lambda 函数还使用通配符 (`cloudformation*`) 来配置 CloudFormation 堆栈，但资源锁定到前缀。`tenant-cluster`
+ 此示例代码的 docker 构建使用 `--platform=linux/amd64` 强制基于 `linux/amd64` 的映像。这是为了确保最终的映像构件适用于 Lambda，它默认使用 x86-64 架构。如果您需要更改目标 Lambda 架构，请务必同时更改 Dockerfiles 和 AWS CDK 代码。有关更多信息，请参阅博客文章：[将 AWS Lambda 函数迁移到基于 ARM 的 AWS Graviton2 处理器](https://aws.amazon.com/blogs/compute/migrating-aws-lambda-functions-to-arm-based-aws-graviton2-processors/)。
+ 堆栈删除过程不会清理堆栈生成的 CloudWatch 日志（日志组和日志）。您必须通过 AWS 管理控制台、Amazon 控制 CloudWatch 台或 API 手动清理日志。

此模式设置为示例。对于生产用途，请评估以下设置并根据您的业务需求进行更改：
+ 为简单起见，本示例中的 [AWS Simple Storage Service（Amazon S3）](https://aws.amazon.com/s3/)存储桶未启用版本控制。根据需要评估和更新设置。
+ 为简单起见，此示例在没有身份验证、授权或节流的情况下设置 [Amazon API Gateway](https://aws.amazon.com/api-gateway/) REST API 端点。对于生产用途，我们建议将系统与业务安全基础设施集成。评估此设置并根据需要添加所需安全设置。
+ 在此租户基础设施示例中，[Amazon Simple Notification Service（Amazon SNS）](https://aws.amazon.com/sns/)和 [Amazon Simple Queue Service（Amazon SQS）](https://aws.amazon.com/sqs/)仅有最低设置。根据 [AWS KMS 密钥政策，每个租户的 AWS 密钥管理服务 (AWS KMS)](https://aws.amazon.com/kms/) 向账户中的[亚马逊 CloudWatch](https://aws.amazon.com/cloudwatch/)和亚马逊 SN [S](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html#compatibility-with-aws-services) 服务开放，供其使用。该设置只是一个占位符示例。根据您的业务使用案例根据需要调整设置。
+ 整个设置包括但不限于 API 终端节点以及使用 AWS 预配置和删除后端租户 CloudFormation，仅涵盖基本的成功路径案例。根据您的业务需求，使用必要的重试逻辑、额外的错误处理逻辑和安全逻辑来评估和更新设置。
+ 在撰写本文时，示例代码使用 up-to-date [cdk-nag](https://github.com/cdklabs/cdk-nag) 进行测试，以检查策略。将来可能会强制执行新的策略。这些新策略可能需要您根据建议手动修改堆栈，然后才能部署堆栈。查看现有代码，确保其符合您的业务需求。
+ 该代码依赖 AWS CDK 生成随机后缀，而不是依赖静态分配的物理名称来创建大部分资源。此设置是为了确保这些资源是唯一的，并且不会与其他堆栈发生冲突。有关更多信息，请参阅 [AWS CDK 文档](https://docs.aws.amazon.com/cdk/v2/guide/resources.html#resources_physical_names)。根据您的业务需求对此进行调整。
+ 此示例代码将 .NET Lambda 构件打包成基于 Docker 的映像，并使用 Lambda 提供的[容器映像运行时系统](https://docs.aws.amazon.com/lambda/latest/dg/csharp-image.html)运行。容器映像运行时系统具有的优势包括：标准传输和存储机制（容器注册表）和更精确的本地测试环境（通过容器映像）。您可以将项目切换为使用 [Lambda 提供的 .NET 运行时系统](https://docs.aws.amazon.com/lambda/latest/dg/lambda-csharp.html)来缩短 Docker 映像的构建时间，但随后您需要设置传输和存储机制，并确保本地设置与 Lambda 设置相匹配。调整代码以适应用户的业务需求。

**产品版本**
+ AWS CDK 版本 2.45.0 或更高版本
+ Visual Studio 2022

## 架构
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-architecture"></a>

**技术堆栈**
+ Amazon API Gateway
+ AWS CloudFormation
+ 亚马逊 CloudWatch
+ Amazon DynamoDB
+ AWS Identity and Access Management (IAM)
+ AWS KMS
+ AWS Lambda
+ Amazon S3
+ Amazon SNS
+ Amazon SQS

**架构**

下图显示了租户堆栈的创建流程。有关控制面板和租户技术堆栈的详情，请参阅*其他信息*部分。

![\[用于在 AWS 上创建租户并为租户预调配基本基础设施的工作流。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/5baef800-fe39-4eb8-b11d-2c23eb3175fc/images/0b579484-b87c-4acb-8c60-8c33c18370e3.png)


**租户堆栈创建流程**

1. 用户向 Amazon API Gateway 托管的 REST API 发送带有 JSON 格式的新租户有效负载（租户名称、租户描述）的 POST API 请求。API 网关处理请求并将其转发到后端 Lambda 租户登录函数。在此示例中，没有授权或身份验证。在生产环境中，此 API 应与 SaaS 基础设施安全系统集成。

1. 租户登录功能会验证请求。然后，它会尝试将租户记录（包括租户名称、生成的租户通用唯一标识符（UUID）和租户描述）存储到 Amazon DynamoDB 租户登录表中。 

1. 在 DynamoDB 存储记录后，DynamoDB 流会启动下游 Lambda 租户基础设施函数。

1. 租户基础设施 Lambda 函数基于收到的 DynamoDB 数据流进行操作。如果流用于 INSERT 事件，则该函数使用流的 NewImage 部分（最新更新记录，“租户名称” 字段）进行调用， CloudFormation 以使用存储在 S3 存储桶中的模板创建新的租户基础架构。 CloudFormation 模板需要租户名称参数。 

1. AWS 根据 CloudFormation 模板和输入参数 CloudFormation 创建租户基础设施。

1. 每个租户基础设施设置都有一个 CloudWatch 警报、一个账单警报和一个警报事件。

1. 警报事件将变成 SNS 主题的消息，该消息由租户的 AWS KMS 密钥加密。

1. SNS 主题将收到的警报消息转发到 SQS 队列，该队列由租户的 AWS KMS 加密以获取加密密钥。

其他系统可以与 Amazon SQS 集成，根据队列中的消息执行操作。在此示例中，为了保持代码的通用性，传入的消息将保留在队列中，需要手动删除。

**租户堆栈删除流程**

1. 用户向 Amazon API Gateway 托管的 REST API 发送带有 JSON 格式的新租户有效负载（租户名称、租户描述）的 DELETE API 请求，REST API 将处理该请求并转发到租户登录功能。在此示例中，没有授权或身份验证。在生产环境中，此 API 将与 SaaS 基础设施安全系统集成。

1. 租户登录功能将验证请求，然后尝试从租户登录表中删除租户记录（租户名称）。 

1. DynamoDB 成功删除记录（该记录存在于表中并已删除）后，DynamoDB 流将启动下游 Lambda 租户基础设施函数。

1. 租户基础设施 Lambda 函数基于收到的 DynamoDB 数据流记录进行操作。如果流是针对REMOVE事件，则该函数使用记录的 OldImage 部分（记录信息和租户名称字段，位于最新更改之前，即删除），根据该记录信息启动对现有堆栈的删除。

1. AWS 根据输入 CloudFormation 删除目标租户堆栈。

## 工具
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-tools"></a>

**AWS 服务**
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) 可帮助您创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [AWS Cloud Development Kit（AWS CDK）](https://docs.aws.amazon.com/cdk/v2/guide/home.html)是一个软件开发框架，可帮助您在代码中定义并预调配 AWS 云基础设施。
+ [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) 是命令行云开发套件，可帮助您与 AWS Cloud Development Kit（AWS CDK）应用程序进行交互。
+ [AWS 命令行界面（AWS CLI）](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)是一种开源工具，它可帮助您通过命令行 Shell 中的命令与 Amazon Web Services 交互。
+ [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) 可帮助您设置 AWS 资源，快速一致地配置这些资源，并在 AWS 账户和区域的整个生命周期中对其进行管理。
+ [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 是一项完全托管式 NoSQL 数据库服务，可提供快速、可预测、可扩展的性能。
+ [AWS Identity and Access Management (AWS IAM) ](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)通过控制验证和授权使用您 AWS 资源的用户，帮助您安全地管理对您 AWS 资源的访问。
+ [AWS Key Management Service (AWS KMS)](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) 可帮助您创建和控制加密密钥，以帮助保护您的数据。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，而无需预置或管理服务器。它仅在需要时运行您的代码，并且能自动扩缩，因此您只需为使用的计算时间付费。
+ [Amazon Simple Storage Service (Amazon S3)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。
+ [Amazon Simple Notiﬁcation Service (Amazon SNS)](https://docs.aws.amazon.com/sns/latest/dg/welcome.html) 可帮助您协调和管理发布者与客户端（包括 Web 服务器和电子邮件地址）之间的消息交换。
+ [Amazon Simple Queue Service (Amazon SQS)](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html) 提供了一个安全、持久且可用的托管队列，它可帮助您集成和分离分布式软件系统与组件。
+ [AWS Toolkit for Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) 是 Visual Studio 集成式开发环境（IDE）的插件。Toolkit for Visual Studio 支持开发、调试和部署使用 Amazon Web Services 的 .NET 应用程序。

**其他工具**
+ [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/ide/whats-new-visual-studio-2022?view=vs-2022) 是一个 IDE，包括编译器、代码完成工具、图形设计器和其他支持软件开发的功能。

**代码**

此模式的代码位于[孤岛模型的 SaaS 架构中的租户登录 APG 示例](https://github.com/aws-samples/tenant-onboarding-in-saas-architecture-for-silo-model-apg-example)存储库中。

## 操作说明
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-epics"></a>

### 设置 AWS CDK
<a name="set-up-aws-cdk"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 验证 Node.js 的安装。 | 要验证 Node.js 是否已安装在本地计算机上，请运行以下命令。<pre>node --version</pre> | AWS 管理员，AWS DevOps | 
| 安装 AWS CDK Toolkit。 | 要在本地计算机上安装 AWS CDK Toolkit，请运行以下命令。<pre>npm install -g aws-cdk</pre>如果未安装 npm，则可以从 [Node.js 站点](https://nodejs.org/en/download/package-manager/)进行安装。 | AWS 管理员，AWS DevOps | 
| 验证 AWS CDK Toolkit 的版本。 | 要验证 AWS CDK Toolkit 版本是否已在本机上正确安装，请运行以下命令。 <pre>cdk --version</pre> | AWS 管理员，AWS DevOps | 

### 查看租户登录控制面板的代码
<a name="review-the-code-for-the-tenant-onboarding-control-plane"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 克隆存储库。 | 克隆[存储库](https://github.com/aws-samples/tenant-onboarding-in-saas-architecture-for-silo-model-apg-example)，然后导航到该 `\tenant-onboarding-in-saas-architecture-for-silo-model-apg-example` 文件夹。在 Visual Studio 2022 中，打开 `\src\TenantOnboardingInfra.sln` 解决方案。打开 `TenantOnboardingInfraStack.cs` 文件并查看代码。以下资源是作为此堆栈的一部分创建的：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | AWS 管理员，AWS DevOps | 
| 查看 CloudFormation 模板。 | 在`\tenant-onboarding-in-saas-architecture-for-silo-model-apg-example\template`文件夹中`infra.yaml`，打开并查看 CloudFormation 模板。此模板将使用从租户登录 DynamoDB 表中检索到的租户名称注入数据。该模板提供了租户特定的基础设施。在此示例中，它配置了 AWS KMS 密钥、亚马逊 SNS、亚马逊 SQS 和警报。 CloudWatch  | AWS 应用程序开发人员 DevOps | 
| 查看租户登录函数。 | 打开 `Function.cs` 并查看租户登录函数的代码，该代码是使用带有 .NET 6（容器映像）蓝图的 Visual Studio AWS Lambda 项目 (.NET Core- C\$1) 模板创建的。打开 `Dockerfile`并查看代码。`Dockerfile` 是一个文本文件，包含构建 Lambda 容器映像的说明。请注意，以下 NuGet 包已作为依赖项添加到`TenantOnboardingFunction`项目中：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | AWS 应用程序开发人员 DevOps | 
| 查看租户 InfraProvisioning 功能。 | 导航到 `\tenant-onboarding-in-saas-architecture-for-silo-model-apg-example\src\InfraProvisioningFunction`。打开 `Function.cs` 并查看租户基础设施预调配函数的代码，该代码是使用带有 .NET 6（容器映像）蓝图的 Visual Studio AWS Lambda 项目（.NET Core-C\$1）模板创建的。打开 `Dockerfile` 并查看代码。请注意，以下 NuGet 包已作为依赖项添加到`InfraProvisioningFunction`项目中：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | AWS 应用程序开发人员 DevOps | 

### 部署 AWS 资源
<a name="deploy-the-aws-resources"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 构建解决方案。 | 要构建解决方案，请执行以下步骤：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html)在构建解决方案之前，确保将 `Amazon.CDK.Lib NuGet` 软件包更新到 `\tenant-onboarding-in-saas-architecture-for-silo-model-apg-example\src\TenantOnboardingInfra` 项目中的最新版本。 | 应用程序开发人员 | 
| 引导 AWS CDK 环境。 | 打开 Windows 命令提示符并导航到 `cdk.json` 文件所在的 AWS CDK 应用程序根文件夹（`\tenant-onboarding-in-saas-architecture-for-silo-model-apg-example`）。运行以下命令进行引导。<pre>cdk bootstrap </pre>如果您已为凭证创建了 AWS 配置文件，请将命令与配置文件一起使用。<pre>cdk bootstrap --profile <profile name><br />  </pre> | AWS 管理员，AWS DevOps | 
| 列出 AWS CDK 堆栈。 | 要列出要作为本项目的一部分创建的所有堆栈，请运行以下命令。<pre>cdk ls<br />cdk ls --profile <profile name></pre>如果您已为凭证创建了 AWS 配置文件，请将命令与配置文件一起使用。<pre>cdk ls --profile <profile name></pre> | AWS 管理员，AWS DevOps | 
| 查看将创建哪些 AWS 资源。 | 要查看将作为此项目的一部分创建的所有 AWS 资源，请运行以下命令。<pre>cdk diff</pre>如果您已为凭证创建了 AWS 配置文件，请将命令与配置文件一起使用。<pre>cdk diff --profile <profile name></pre> | AWS 管理员，AWS DevOps | 
| 使用 AWS CDK 部署所有 AWS 资源。 | 要部署所有 AWS 资源，请运行以下命令。<pre>cdk deploy --all --require-approval never</pre>如果您已为凭证创建了 AWS 配置文件，请将命令与配置文件一起使用。<pre>cdk deploy --all --require-approval never --profile <profile name></pre>部署完成后，从命令提示符的输出部分复制 API URL，如以下示例所示。<pre>Outputs:<br />TenantOnboardingInfraStack.TenantOnboardingAPIEndpoint42E526D7 = https://j2qmp8ds21i1i.execute-api.us-west-2.amazonaws.com/prod/</pre> | AWS 管理员，AWS DevOps | 

### 验证功能
<a name="verify-the-functionality"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建新租户。 | 要创建新租户，请发送以下 curl 请求。<pre>curl -X POST <TenantOnboardingAPIEndpoint* from CDK Output>tenant -d '{"Name":"Tenant123", "Description":"Stack for Tenant123"}'</pre>将占位符 `<TenantOnboardingAPIEndpoint* from CDK Output>` 更改为 AWS CDK 中的实际值，如以下示例所示。<pre>curl -X POST https://j2qmp8ds21i1i.execute-api.us-west-2.amazonaws.com/prod/tenant -d '{"Name":"Tenant123", "Description":"test12"}'</pre>以下示例显示了输出。<pre>{"message": "A new tenant added - 5/4/2022 7:11:30 AM"}</pre> | 应用程序开发人员、AWS 管理员、AWS DevOps | 
| 在 DynamoDB 中验证新创建租户的详细信息。 | 要验证 DynamoDB 中新创建租户的详细信息，请执行以下步骤。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | 应用程序开发人员、AWS 管理员、AWS DevOps | 
| 验证为新租户创建的堆栈。 | 验证新堆栈是否已成功创建并根据 CloudFormation 模板为新创建的租户配置了基础架构。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | 应用程序开发人员、AWS 管理员、AWS DevOps | 
| 删除租户堆栈。 | 要删除租户堆栈，请发送以下 curl 请求。<pre>curl -X DELETE <TenantOnboardingAPIEndpoint* from CDK Output>tenant/<Tenant Name from previous step></pre>将占位符 `<TenantOnboardingAPIEndpoint* from CDK Output>` 更改为 AWS CDK 中的实际值，然后更改 `<Tenant Name from previous step>` 为上一个租户创建步骤中的实际值，如以下示例所示。<pre>curl -X DELETE https://j2qmp8ds21i1i.execute-api.us-west-2.amazonaws.com/prod/tenant/Tenant123</pre>以下示例显示了输出。<pre>{"message": "Tenant destroyed - 5/4/2022 7:14:48 AM"}</pre> | 应用程序开发人员、AWS DevOps、AWS 管理员 | 
| 验证现有租户的堆栈删除。 | 要验证现有租户的堆栈是否已删除，请执行以下步骤：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html) | 应用程序开发人员、AWS 管理员、AWS DevOps | 

### 清理
<a name="clean-up"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 摧毁环境。 | 在清理堆栈之前，请确保满足以下条件：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk.html)测试完成后，可以通过运行以下命令使用 AWS CDK 销毁所有堆栈和相关资源。<pre>cdk destroy --all;</pre>如果您已为凭证创建了 AWS 配置文件，请使用配置文件。确认堆栈删除提示以删除堆栈。 | AWS 管理员，AWS DevOps | 
| 清理 Amazon CloudWatch 日志。 | 堆栈删除过程不会清除堆栈生成的日 CloudWatch 志（日志组和日志）。使用 CloudWatch 控制台或 API 手动清理 CloudWatch 资源。 | 应用程序开发人员、AWS DevOps、AWS 管理员 | 

## 相关资源
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-resources"></a>
+ [AWS CDK .NET 讲习会](https://cdkworkshop.com/40-dotnet.html)
+ [在 C\$1 中使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-csharp.html)
+ [CDK .NET 参考资料](https://docs.aws.amazon.com/cdk/api/v2/dotnet/api/index.html)

## 附加信息
<a name="tenant-onboarding-in-saas-architecture-for-the-silo-model-using-c-and-aws-cdk-additional"></a>

**控制面板技术堆栈**

用 .NET 编写的 CDK 代码用于预调配控制面板基础设施，该基础设施由以下资源组成：

1. **API Gateway**

   用作控制面板堆栈的 REST API 入口点。

1. **租户登录 Lambda 函数**

   此 Lambda 函数由 API 网关使用 m 方法启动。

   POST 方法 API 请求会导致（`tenant name`、`tenant description`）被插入到 DynamoDB 表 `Tenant Onboarding` 中。

   在此代码示例中，租户名称也用作租户堆栈名称的一部分以及该堆栈中资源的名称。这是为了使这些资源更易于识别。此租户名称在设置中必须是唯一的，以避免冲突或错误。详细的输入验证设置在 [IAM 角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)文档和*限制*部分中进行了说明。

   只有在表中的任何其他记录中未使用租户名称时，DynamoDB 表的持久化过程才会成功。

   在这种情况下，租户名称是该表的分区键，因为只有分区键可以用作 `PutItem` 条件表达式。

   如果以前从未记录过租户名称，则该记录将成功保存到表格中。

   但是，如果表中的现有记录已使用租户名称，则操作将失败并启动 DynamoDB `ConditionalCheckFailedException` 异常。该异常将用于返回一条失败消息（`HTTP BadRequest`），指示租户名称已存在。

   `DELETE` 方 法 API 请求将从 `Tenant Onboardin`g 表中删除特定租户名称的记录。

   即使该记录不存在，本示例中的 DynamoDB 记录删除也会成功。

   如果目标记录存在并被删除，它将创建一个 DynamoDB 数据流记录。否则，将不会创建任何下游记录。

1. **在 Amazon DynamoDB Streams 启用时的租户登录 DynamoDB**

   这会记录租户元数据信息，任何记录的保存或删除都会将数据流发送到下游 `Tenant Infrastructure`Lambda 函数。 

1. **租户基础设施 Lambda 函数**

   此 Lambda 函数由上一步中的 DynamoDB 数据流记录启动。如果记录是针对某个`INSERT`事件的，则它会调用 AWS CloudFormation 以使用存储在 S3 存储桶中的 CloudFormation 模板创建新的租户基础设施。如果该记录用于 `REMOVE`，则它会根据流记录的 `Tenant Name` 字段启动对现有堆栈的删除。

1. **S3 存储桶**

   这是用来存储 CloudFormation 模板的。

1. **每个 Lambda 函数的 IAM 角色和一个服务角色 CloudFormation**

   每个 Lambda 函数都有其唯一的 IAM 角色，该角色具有完成任务的[最低权限许可](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)。例如，`Tenant On-boarding`Lambda 函数 read/write 可以访问 DynamoDB，而 Lambda 函数只能读取 Dyn `Tenant Infrastructure` amoDB 流。

   为租户堆栈置备创建了自定义 CloudFormation 服务角色。此服务角色包含 CloudFormation 堆栈配置的其他权限（例如，AWS KMS 密钥）。这会在 Lambda 之间划分角色 CloudFormation ，以避免对单个角色（基础设施 Lambda 角色）拥有所有权限。

   允许强大操作（例如创建和删除 CloudFormation 堆栈）的权限被锁定，并且仅允许使用以开头的`tenantcluster-`资源。AWS KMS 是个例外，因为它的资源命名约定。从 API 摄取的租户名称将与其他验证检查一起添加到 `tenantcluster-` 前面（仅带破折号的字母数字，并且限制在 30 个字符以内，以适应大多数 AWS 资源命名）。这样可以确保租户名称不会意外导致核心基础设施堆栈或资源中断。

**租户技术堆栈**

 CloudFormation 模板存储在 S3 存储桶中。[该模板预置了租户特定的 AWS KMS 密钥、 CloudWatch 警报、SNS 主题、SQS 队列和 SQS 策略。](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-using-identity-based-policies.html)

AWS KMS 密钥用于其消息的 Amazon SNS 和 Amazon SQS 的数据加密。[SNS2 和 AwsSolutions AwsSolutions-](https://github.com/cdklabs/cdk-nag/blob/main/RULES.md) 的安全实践SQS2建议您设置带加密功能的 Amazon SNS 和 Amazon SQS。但是，使用 AWS 托管密钥时， CloudWatch 警报不适用于 Amazon SNS，因此在这种情况下，您必须使用客户托管密钥。有关更多信息，请参阅 [AWS Knowledge Center](https://aws.amazon.com/premiumsupport/knowledge-center/cloudwatch-receive-sns-for-alarm-trigger/)。

在 Amazon SQS 队列上使用 SQS 策略来允许创建的 SNS 主题将消息传送到队列。如果没有 SQS 策略，访问将被拒绝。有关更多信息，请参阅 [Amazon SNS 文档](https://docs.aws.amazon.com/sns/latest/dg/subscribe-sqs-queue-to-sns-topic.html#SendMessageToSQS.sqs.permissions)。

# 使用 CQRS 和事件溯源将整体分解为微服务
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing"></a>

*Rodolfo Jr. Cerrada、Dmitry Gulin 和 Tabby Ward，Amazon Web Services*

## Summary
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-summary"></a>

此示例介绍了两种模式，使用命令查询责任分离 (CQRS) 模式和事件溯源模式。CQRS 模式将命令模型和查询模型职责分开。事件溯源模式利用异步事件驱动通信来改善整体用户体验。

您可使用 CQRS 和 Amazon Web Services (AWS) 服务独立维护和扩展每个数据模型，同时将您的整体应用程序重构为微服务架构。然后，您可使用事件溯源模式，将数据从命令数据库同步至查询数据库。

此示例使用包含解决方案 (\$1.sln) 文件的示例代码，您可以使用最新版本 Visual Studio 打开该文件。此示例包含奖励 API 代码，用于展示 CQRS 和事件溯源在 AWS 无服务器和传统或本地应用程序中的工作方式。

要了解有关 CQRS 和事件溯源的更多信息，请参阅[其他信息](#decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-additional)部分。

## 先决条件和限制
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-prereqs"></a>

**先决条件**
+ 一个活跃的 AWS 账户
+ Amazon CloudWatch
+ Amazon DynamoDB 表
+ Amazon DynamoDB Streams
+ AWS Identity and Access Management (IAM) 访问密钥。有关更多信息，请参阅*相关资源*部分中的视频
+ AWS Lambda
+ 熟悉 Visual Studio
+ 熟悉 AWS Toolkit for Visual Studio；有关更多信息，请参阅*相关资源*部分的*AWS Toolkit for Visual Studio 演示*

**产品版本**
+ [Visual Studio 2019 Community Edition](https://visualstudio.microsoft.com/downloads/)。
+ [AWS Toolkit for Visual Studio 2019](https://aws.amazon.com/visualstudio/)。
+ .NET Core 3.1。此组件是 Visual Studio 安装选项之一。若要在安装过程中纳入 .NET Core，请选择 **NET Core 跨平台开发**。

**限制**
+ 传统本地应用程序 (ASP.NET Core Web API 和数据访问对象) 的示例代码不附带数据库。但是，它附带了 `CustomerData` 内存对象，该对象充当模拟数据库。所提供的代码适用于测试模式。

## 架构
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-architecture"></a>

**源技术堆栈**
+ ASP.NET Core Web API 项目
+ IIS Web 服务器
+ 数据访问对象
+ CRUD 模型

**源架构**

在源架构中，CRUD 模型在一个应用程序中同时包含命令与查询接口。有关示例代码，请参阅 `CustomerDAO.cs`（附后）。

![\[应用程序、服务接口、客户 CRUD 模型以及数据库之间的连接。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/1cd3a84c-12c7-4306-99aa-23f2c53d3cd3.png)


**目标技术堆栈**
+ Amazon DynamoDB
+ Amazon DynamoDB Streams
+ AWS Lambda
+ （可选）Amazon API Gateway
+ （可选）Amazon Simple Notiﬁcation Service (Amazon SNS)

**目标架构**

在目标架构中，命令与查询接口是分开的。下图所示的架构可使用 API Gateway 和 Amazon SNS 进行扩展。有关更多信息，请参阅[其他信息](#decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-additional)部分。

![\[与无服务器客户命令和客户查询微服务连接的应用程序。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/1c665697-e3ac-4ef4-98d0-86c2cbf164c1.png)


1. 命令 Lambda 函数对数据库执行写入操作，如创建、更新或删除。

1. 查询 Lambda 函数对数据库执行读取操作，如获取或选择。

1. 此 Lambda 函数处理来自命令数据库的 DynamoDB 数据流，并更新查询数据库以获取更改。

## 工具
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-tools"></a>

**工具**
+ [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) – Amazon DynamoDB 是一种全托管 NoSQL 数据库服务，提供快速而可预测的性能，能够实现无缝扩展。
+ [Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html) - DynamoDB Streams 捕获在任何 DynamoDB Streams 表中按时间排序的项目级修改序列。它还会将这类信息存储在日志中长达 24 个小时。静态加密会加密 DynamoDB 流中的数据。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) – AWS Lambda 是一项计算服务，支持无需预置或管理服务器即可运行代码。只有在需要时 Lambda 才运行您的代码，并且能自动扩缩，从每天几个请求扩展到每秒数千个请求。您只需为消耗的计算时间付费 - 代码未运行时不产生费用。
+ [AWS 管理控制台](https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/learn-whats-new.html) — AWS 管理控制台是一款 Web 应用程序，包含多种用于管理 Amazon Web Services 的服务控制台。
+ [Visual Studio 2019 Community Edition](https://visualstudio.microsoft.com/downloads/) – Visual Studio 2019 是一个集成式开发环境（IDE）。开源参与者可免费使用社区版。在此示例中，您将使用 Visual Studio 2019 Community Edition 打开、编译和运行示例代码。仅供查看，您可以使用任何文本编辑器或 [Visual Studio Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html)。
+ [AWS Toolkit for Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) – AWS Toolkit for Visual Studio 是 Visual Studio IDE 的一个插件。AWS Toolkit for Visual Studio 可让您更轻松地开发、调试和部署使用 Amazon Web Services .NET 应用程序。

**代码**

示例代码附后。有关部署示例代码的说明，请参阅*操作*部分。

## 操作说明
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-epics"></a>

### 打开和生成解决方案
<a name="open-and-build-the-solution"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 打开解决方案。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员 | 
| 构建解决方案。 | 打开解决方案的上下文（右键单击）菜单，然后选择**生成解决方案**。这将生成和编译解决方案的所有项目。它应该可成功编译。Visual Studio 解决方案资源管理器应该显示目录结构。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员 | 

### 构建 DynamoDB 表格
<a name="build-the-dynamodb-tables"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 提供凭证。 | 如果您还没有访问密钥，请查看*相关资源*部分中的视频。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员、数据工程师、数据库管理员 | 
| 构建 项目。 | 要生成项目，请打开 **AwS.APG.CQRSES.Build** 项目的上下文（右键单击）菜单，然后选择**生成**。 | 应用程序开发人员、数据工程师、数据库管理员 | 
| 生成和填充表格。 | 若要生成表格并向其中填充种子数据，打开 **AwS.APG.CQRSES.Build** 项目的上下文（右键点击）菜单，然后选择**调试**、**启动新实例**。 | 应用程序开发人员、数据工程师、数据库管理员 | 
| 验证表结构与数据。 | 若要进行验证，请导航至 **AWS 各区服务浏览器**，然后展开 **Amazon DynamoDB**。它应该显示此表格。打开每个表，以显示示例数据。 | 应用程序开发人员、数据工程师、数据库管理员 | 

### 运行本地测试
<a name="run-local-tests"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 构建 CQRS 项目。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员、测试工程师 | 
| 生成事件溯源项目。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员、测试工程师 | 
| 运行测试。 | 要运行所有测试，请选择**查看**、**测试资源管理器**，然后选择**在视图中运行所有测试**。所有测试都应通过，通过后以绿色复选标记图标表示。  | 应用程序开发人员、测试工程师 | 

### 将 CQRS Lambda 函数发布至 AWS
<a name="publish-the-cqrs-lambda-functions-to-aws"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 发布第一个 Lambda 函数。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发者、 DevOps 工程师 | 
| 验证函数上传情况。 | （可选）您可以通过导航到 AWS 各区服务浏览器并展开 **AWS Lambda** 来验证函数是否已成功加载。若要打开测试窗口，请选择 Lambda 函数（双击）。 | 应用程序开发者、 DevOps 工程师 | 
| 测试 Lambda 函数。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html)所有 CQRS Lambda 项目都位于 ` CQRS AWS Serverless\CQRS\Command Microservice` 和 `CQRS AWS Serverless\CQRS\Command Microservice` 解决方案文件夹下。有关解决方案目录和项目，请参阅[其他信息](#decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-additional)部分中的**源代码目录**。 | 应用程序开发者、 DevOps 工程师 | 
| 发布其余函数。 | 对以下项目重复之前的步骤：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发者、 DevOps 工程师 | 

### 将 Lambda 函数设置为事件侦听器
<a name="set-up-the-lambda-function-as-an-event-listener"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 发布客户和奖励 Lambda 事件的处理程序。 | 若要发布每个事件处理程序，请按前述操作中的步骤进行操作。这些项目位于 `CQRS AWS Serverless\Event Source\Customer Event` 和 `CQRS AWS Serverless\Event Source\Reward Event` 解决方案文件夹下。有关更多信息，请参阅[其他信息](#decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-additional)中的*源代码目录*部分。 | 应用程序开发人员 | 
| 附加事件溯源 Lambda 事件侦听器。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html)侦听器成功连接至 DynamoDB 表后，它将显示在 Lambda 设计器页面上。 | 应用程序开发人员 | 
| 发布并附加 EventSourceReward Lambda 函数。 | **要发布并附加 `EventSourceReward` Lambda 函数，请重复前两个故事中的步骤，**cqrses-reward-cmd**从 DynamoDB 表下拉列表中进行选择。** | 应用程序开发人员 | 

### 测试和验证 DynamoDB 流与 Lambda 触发器
<a name="test-and-validate-the-dynamodb-streams-and-lambda-trigger"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 测试流数据和 Lambda 触发器。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员 | 
| 使用 DynamodDB 奖励查询表验证。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员 | 
| 使用 CloudWatch 日志进行验证。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing.html) | 应用程序开发人员 | 
| 验证 EventSourceCustomer 触发器。 | 要验证`EventSourceCustomer`触发器，请使用`EventSourceCustomer`触发器的相应客户表和 CloudWatch 日志，重复此长篇故事中的步骤。 | 应用程序开发人员 | 

## 相关资源
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-resources"></a>

**参考**
+ [Visual Studio 2019 Community Edition 下载](https://visualstudio.microsoft.com/downloads/)
+ [AWS Toolkit for Visual Studio 下载](https://aws.amazon.com/visualstudio/)
+ [AWS Toolkit for Visual Studio 用户指南](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html)
+ [Serverless on AWS](https://aws.amazon.com/serverless/)
+ [DynamoDB 用例与设计模式](https://aws.amazon.com/blogs/database/dynamodb-streams-use-cases-and-design-patterns/)
+ [Martin Fowler CQRS](https://martinfowler.com/bliki/CQRS.html)
+ [Martin Fowler 事件溯源](https://martinfowler.com/eaaDev/EventSourcing.html)

**视频**
+ [AWS Toolkit for Visual Studio 演示](https://www.youtube.com/watch?v=B190tcu1ERk)
+ [如何为新 IAM 用户创建访问密钥 ID？](https://www.youtube.com/watch?v=665RYobRJDY)

## 附加信息
<a name="decompose-monoliths-into-microservices-by-using-cqrs-and-event-sourcing-additional"></a>

**CQRS 和事件溯源**

*CQRS*

CQRS 模式将单个概念操作模型（例如数据访问对象单个 CRUD（创建、读取、更新、删除）模型）分离为命令和查询操作模型。命令模型是指任何更改状态的操作，如创建、更新或删除。查询模型是指任何返回值操作。

![\[包含服务接口、CRUD 模型以及数据库的架构。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/3f64756d-681e-4f0e-8034-746263d857b2.png)


1. 客户 CRUD 模型包含以下接口：
   + `Create Customer()`
   + `UpdateCustomer()`
   + `DeleteCustomer()`
   + `AddPoints()`
   + `RedeemPoints()`
   + `GetVIPCustomers()`
   + `GetCustomerList()`
   + `GetCustomerPoints()`

随着您的需求变得更加复杂，您可放弃这种单一模型方法。CQRS 使用命令模型和查询模型分离写入和读取数据的职责。这样就可以独立维护和管理数据。通过明确职责分工，对每个模型的增强不会影响其他模型。这种分离可改善维护和性能，并随着应用程序的增长降低其复杂性。

![\[该应用程序分为命令模型与查询模型，共享一个数据库。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/12db023c-eb81-4c27-bbb9-b085b13176ae.png)


 

1. 客户命令模型接口：
   + `Create Customer()`
   + `UpdateCustomer()`
   + `DeleteCustomer()`
   + `AddPoints()`
   + `RedeemPoints()`

1. 客户查询模型接口：
   + `GetVIPCustomers()`
   + `GetCustomerList()`
   + `GetCustomerPoints()`
   + `GetMonthlyStatement()`

有关示例代码，请参阅*源代码目录*。

然后，CQRS 模式可解耦数据库。这种解耦使每项服务能完全独立，这是微服务架构的主要组成部分。

![\[命令模型和查询模型数据库分开。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/016dbfa8-3bd8-42ee-afa1-38a98986c7d5.png)


 在 Amazon Web Services Cloud 中使用 CQRS，您可进一步优化每项服务。例如，您可设置不同的计算设置，或者在无服务器或基于容器的微服务之间进行选择。您可以用 Amazon 替换您的本地缓存 ElastiCache。如果您有本地 publish/subscribe 消息，则可以将其替换为亚马逊简单通知服务 (Amazon SNS) Service。此外，您还可以利用 pay-as-you-go定价和各种 AWS 服务，这些服务只需按实际用量付费。

CQRS 可提供以下优势：
+ 独立扩展 — 每个模型都可调整其扩展策略，以满足服务要求和需求。与高性能应用程序类似的是，将读写分离可以使模型独立扩展，以满足每种需求。您还可以添加或减少计算资源，以满足模型的可扩展性需求，而不影响另一种模型。
+ 独立维护 — 查询模型和命令模型的分离改进了模型的可维护性。您可在不影响另一个模型的情况下对一个模型进行代码更改和增强。
+ 安全 - 可以更轻松地将权限和策略应用于不同的读取和写入模型。
+ 优化读取 - 您可定义针对查询进行优化的架构。例如，您可为聚合数据定义一个架构，为事实表定义一个单独的架构。
+ 集成 – CQRS 非常适合基于事件的编程模型。
+ 托管复杂性 — 查询和命令模型的分离适合复杂的领域。

使用 CQRS 时，请记住以下注意事项：
+ CQRS 模式仅适用于应用程序的特定部分，不适用于整个应用程序。如果在不适合该模式的领域实施，它会降低生产力、增加风险和引入复杂性。
+ 该模式最适合具有不平衡读写操作的常用模型。
+ 对于读取量大的应用程序（例如需要时间处理的大型报告），CQRS 使您可以选择正确的数据库，并创建一个架构以存储聚合数据。通过仅处理一次报告数据并将其转储到聚合表中，可以提高读取和查看报告的响应时间。
+ 对于写入量大的应用程序，您可以配置数据库进行写入操作，并允许命令微服务在写入需求增加时独立扩展。有关示例，请参阅 `AWS.APG.CQRSES.CommandRedeemRewardLambda` 和 `AWS.APG.CQRSES.CommandAddRewardLambda` 微服务。

*事件溯源*

下一步，在运行命令时使用事件溯源同步查询数据库。例如，请考虑以下事件：
+ 添加客户奖励积分，要求更新查询数据库中的客户总奖励积分或汇总奖励积分。
+ 在命令数据库中更新客户姓氏，这要求更新查询数据库中的代理客户信息。

在传统 CRUD 模型中，您可通过锁定数据直到完成事务来确保数据的一致性。在事件溯源中，通过发布一系列事件来同步数据，订阅用户将使用这些事件来更新其各自的数据。

事件溯源模式可确保并记录一系列数据操作，并通过一系列事件将其发布。这些事件表示：该事件的订阅用户为保持记录更新而必须处理的一系列数据更改。这些事件由订阅用户使用，用于同步订阅用户数据库的数据。在本例中，就是查询数据库。

下图显示了 AWS 上与 CQRS 共用的事件溯源。

![\[使用 AWS 无服务器服务的 CQRS 和事件溯源模式的微服务架构。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/cc9bc84a-60b4-4459-9a5c-2334c69dbb4e.png)


1. 命令 Lambda 函数对数据库执行写入操作，如创建、更新或删除。

1. 查询 Lambda 函数对数据库执行读取操作，如获取或选择。

1. 此 Lambda 函数处理来自命令数据库的 DynamoDB 数据流，并更新查询数据库以获取更改。您也可使用此功能向 Amazon SNS 发布消息，以便其订阅用户可以处理数据。

1. （可选）Lambda 事件订阅用户处理 Amazon SNS 发布的消息，并更新查询数据库。

1. （可选）Amazon SNS 会发送有关写入操作的电子邮件通知。

在 AWS 上，查询数据库可通过 DynamoDB Streams 进行同步。DynamoDB 可在 DynamoDB 表中捕获按时间排序的项目级修改序列，并在 24 个小时内持久存储信息。

激活 DynamoDB Streams 使数据库能发布一系列事件，从而使事件溯源模式成为可能。事件溯源模式添加事件订阅用户。事件订阅用户应用程序使用事件，并根据订阅用户的责任进行处理。在上图中，事件订阅用户将更改推送至查询 DynamoDB 数据库以保持数据同步。Amazon SNS、消息代理和事件订阅用户应用程序的使用，使架构保持独立。

事件溯源包括以下优势：
+ 事务数据的一致性
+ 可靠的审计跟踪记录和操作历史记录，可用于监控数据中采取的操作
+ 允许微服务等分布式应用程序在整个环境中同步数据
+ 当状态发生变化时，都能可靠地发布事件
+ 重建或重现过去状态
+ 松散耦合实体，用于交换事件以从单体应用程序迁移至微服务
+ 减少由并发更新引起的冲突；事件溯源避免了直接在数据存储中更新对象的要求
+ 通过将任务和事件脱钩，来实现灵活性和可扩展性
+ 外部系统更新
+ 单个事件中管理多项任务

使用事件溯源时，请记住以下注意事项：
+ 由于源订阅用户数据库之间的数据更新会有延迟，因此撤消更改的唯一方法是向事件存储中添加补偿事件。
+ 因为其编程风格不同，因此实现事件溯源需要学习曲线。

**测试数据**

成功部署后，使用以下数据测试 Lambda 函数。

**CommandCreate Customer**

```
{  "Id":1501,  "Firstname":"John",  "Lastname":"Done",  "CompanyName":"AnyCompany",  "Address": "USA",  "VIP":true }
```

**CommandUpdate Customer**

```
{  "Id":1501,  "Firstname":"John",  "Lastname":"Doe",  "CompanyName":"Example Corp.",  "Address": "Seattle, USA",  "VIP":true }
```

**CommandDelete Customer**

输入客户 ID 为请求数据。例如，如果客户 ID 为 151，则输入 151 为请求数据。

```
151
```

**QueryCustomerList**

此值为空。当它被调用时，将返回所有客户。

**CommandAddReward**

这将为身份为 1 的客户 (Richard) 增加 40 点积分。

```
{
  "Id":10101,
  "CustomerId":1,
  "Points":40
}
```

**CommandRedeemReward**

这将扣除 ID 为 1 的买家 (Richard) 的 15 点积分。

```
{
  "Id":10110,
  "CustomerId":1,
  "Points":15
}
```

**QueryReward**

输入客户 ID。例如，为 Richard 输入 1，为 Arnav 输入 2，为 Shirley 输入 3。

```
2 
```

**源代码目录**

使用下表指导，以了解 Visual Studio 解决方案的目录结构。 

*CQRS 本地代码示例解决方案目录*

![\[命令和查询服务已扩展的解决方案目录。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/4811c2c0-643b-410f-bb87-0b86ec5e194c.png)


**客户 CRUD 模型**

CQRS On-Premises Code Sample\$1CRUD Model\$1AWS.APG.CQRSES.DAL 项目

**客户 CRUD 模型的 CQRS 版本**
+ 客户命令：`CQRS On-Premises Code Sample\CQRS Model\Command Microservice\AWS.APG.CQRSES.Command` 项目
+ 客户查询：`CQRS On-Premises Code Sample\CQRS Model\Query Microservice\AWS.APG.CQRSES.Query` 项目

**命令和查询微服务**

Command 微服务位于解决方案文件夹 `CQRS On-Premises Code Sample\CQRS Model\Command Microservice`：
+ `AWS.APG.CQRSES.CommandMicroservice` ASP.NET Core API 项目充当使用者与服务交互的入口。
+ `AWS.APG.CQRSES.Command` .NET Core 项目是托管与命令相关的对象和接口的对象。

查询微服务位于解决方案文件夹 `CQRS On-Premises Code Sample\CQRS Model\Query Microservice`：
+ `AWS.APG.CQRSES.QueryMicroservice` ASP.NET Core API 项目充当使用者与服务交互的入口。
+ `AWS.APG.CQRSES.Query` .NET Core 项目是托管与查询相关的对象和接口的对象。

*CQRS AWS 无服务器代码解决方案目录*

![\[显示微服务和事件源已扩展的解决方案目录。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f1bc700-def4-4201-bb2d-f1fa27404f15/images/23f8655c-95ad-422c-b20a-e29dc145e995.png)


 

此代码是使用 AWS 无服务器服务本地代码的 AWS 版本。

在 C\$1 .NET Core 中，每个 Lambda 函数都由一个 .NET 核心项目表示。在此模式示例代码中，命令和查询模型中的每个接口都包含一个单独的项目。

**使用 Amazon Web Services 的 CQRS**

您可在 `CQRS AWS Serverless\CQRS` 文件夹中找到使用 AWS 无服务器服务的 CQRS 的根解决方案目录。该示例包括两个模型：即“客户” 和“奖励”。

客户和奖励的 Lambda 命令函数位于 `CQRS\Command Microservice\Customer` 和 `CQRS\Command Microservice\Reward` 文件夹。它们包含以下 Lambda 项目：
+ 客户命令：`CommandCreateLambda`、`CommandDeleteLambda` 和 `CommandUpdateLambda`
+ 奖励命令：`CommandAddRewardLambda` 和 `CommandRedeemRewardLambda`

“客户”和“奖励”的 Lambda 查询函数位于 `CQRS\Query Microservice\Customer` 和 `CQRS\QueryMicroservice\Reward` 文件夹下。它们包含 `QueryCustomerListLambda` 和 `QueryRewardLambda` Lambda 项目。

**CQRS 测试项目**

测试项目位于 `CQRS\Tests` 文件夹下。该项目包含用于自动测试 CQRS Lambda 函数的测试脚本。

**使用 Amazon Web Services 的事件溯源**

以下 Lambda 事件处理程序由客户和奖励 DynamoDB 流启动，旨在用于处理和同步查询表中的数据。
+ `EventSourceCustomer` Lambda 函数映射到客户表 (`cqrses-customer-cmd`) DynamoDB 流。
+ `EventSourceReward`Lambda 函数映射到奖励表 (`cqrses-reward-cmd`) DynamoDB 流。

## 附件
<a name="attachments-9f1bc700-def4-4201-bb2d-f1fa27404f15"></a>

要访问与此文档相关联的其他内容，请解压以下文件：[attachment.zip](samples/p-attach/9f1bc700-def4-4201-bb2d-f1fa27404f15/attachments/attachment.zip)

# 更多模式
<a name="modernization-more-patterns-pattern-list"></a>

**Topics**
+ [使用 AWS PrivateLink 和 Network Load Balancer 在 Amazon EKS 上私密访问容器应用程序](access-container-applications-privately-on-amazon-eks-using-aws-privatelink-and-a-network-load-balancer.md)
+ [通过 AWS Systems Manager 自动添加或更新 Windows 注册表项](automate-adding-or-updating-windows-registry-entries-using-aws-systems-manager.md)
+ [使用 DR Orchestrator Framework 自动执行跨区域故障转移和故障恢复](automate-cross-region-failover-and-failback-by-using-dr-orchestrator-framework.md)
+ [使用 CI/CD 管道自动构建 Java 应用程序并将其部署到 Amazon EKS](automatically-build-and-deploy-a-java-application-to-amazon-eks-using-a-ci-cd-pipeline.md)
+ [使用 AWS CDK 自动为微服务构建 CI/CD 管道和 Amazon ECS 集群](automatically-build-ci-cd-pipelines-and-amazon-ecs-clusters-for-microservices-using-aws-cdk.md)
+ [使用 BMC AMI Cloud Data 备份大型机数据并将其存档至 Amazon S3](back-up-and-archive-mainframe-data-to-amazon-s3-using-bmc-ami-cloud-data.md)
+ [使用 Amazon A EC2 uto Scaling 和 Systems Manager 构建 Micro Focus 企业服务器 PAC](build-a-micro-focus-enterprise-server-pac-with-amazon-ec2-auto-scaling-and-systems-manager.md)
+ [使用 Amazon 构建企业数据网格 DataZone AWS CDK，以及 AWS CloudFormation](build-enterprise-data-mesh-amazon-data-zone.md)
+ [对经过 Blu Age 现代化改造的大型机工作负载进行容器化](containerize-mainframe-workloads-that-have-been-modernized-by-blu-age.md)
+ [使用 Python 在 AWS 上将 EBCDIC 数据转换并解压为 ASCII](convert-and-unpack-ebcdic-data-to-ascii-on-aws-by-using-python.md)
+ [使用 Micro Focus 转换具有复杂记录布局的大型机数据文件](convert-mainframe-data-files-with-complex-record-layouts-using-micro-focus.md)
+ [使用 AWS Amplify、Angular 和模块联合为微前端创建门户](create-amplify-micro-frontend-portal.md)
+ [使用 Elastic Beanstalk 部署容器](deploy-containers-by-using-elastic-beanstalk.md)
+ [通过使用兼容 PostgreSQL 的 Aurora 全局数据库来模拟 Oracle 灾难恢复](emulate-oracle-dr-by-using-a-postgresql-compatible-aurora-global-database.md)
+ [在 Quick Sight 中使用 AWS Mainframe Modernization 和 Amazon Q 生成数据见解](generate-data-insights-by-using-aws-mainframe-modernization-and-amazon-q-in-quicksight.md)
+ [在 Quick Sight 中使用 AWS Mainframe Modernization 和 Amazon Q 生成 Db2 z/OS 数据见解](generate-db2-zos-data-insights-aws-mainframe-modernization-amazon-q-in-quicksight.md)
+ [迁移到 Amazon ECR 存储库时自动识别重复的容器映像](identify-duplicate-container-images-automatically-when-migrating-to-ecr-repository.md)
+ [通过 K8sGPT 和 Amazon Bedrock 集成，实施人工智能驱动的 Kubernetes 诊断和问题排查](implement-ai-powered-kubernetes-diagnostics-and-troubleshooting-with-k8sgpt-and-amazon-bedrock-integration.md)
+ [在 AWS Blu Age 的现代化大型机应用程序中实施基于 Microsoft Entra ID 的身份验证](implement-entra-id-authentication-in-aws-blu-age-modernized-mainframe-application.md)
+ [在 Amazon API Gateway 中使用自定义域实施基于路径的 API 版本控制](implement-path-based-api-versioning-by-using-custom-domains.md)
+ [使用 Oracle SQL Developer 和 AWS SCT 以增量方式从 Amazon RDS for Oracle 迁移至 Amazon RDS for PostgreSQL](incrementally-migrate-from-amazon-rds-for-oracle-to-amazon-rds-for-postgresql-using-oracle-sql-developer-and-aws-sct.md)
+ [将 Stonebranch Universal Controller 与 AWS Mainframe Modernization 集成](integrate-stonebranch-universal-controller-with-aws-mainframe-modernization.md)
+ [管理多个 Amazon Web Services account 和 AWS 区域中的 AWS Service Catalog 产品](manage-aws-service-catalog-products-in-multiple-aws-accounts-and-aws-regions.md)
+ [将 AWS 成员账户从迁移 AWS Organizations 到 AWS Control Tower](migrate-an-aws-member-account-from-aws-organizations-to-aws-control-tower.md)
+ [使用 Connect from Precisely 将 VSAM 文件迁移和复制到 Amazon RDS 或 Amazon MSK](migrate-and-replicate-vsam-files-to-amazon-rds-or-amazon-msk-using-connect-from-precisely.md)
+ [使用 AWS DMS 从 SAP ASE 迁移至 Amazon RDS for SQL Server](migrate-from-sap-ase-to-amazon-rds-for-sql-server-using-aws-dms.md)
+ [将 Oracle 外部表迁移到 Amazon Aurora PostgreSQL-Compatible](migrate-oracle-external-tables-to-amazon-aurora-postgresql-compatible.md)
+ [使用实现 CardDemo 大型机应用程序的现代化 AWS Transform](modernize-carddemo-mainframe-app.md)
+ [使用 AWS Transform 和 Terraform 实现大型机应用程序的现代化和部署](modernize-mainframe-app-transform-terraform.md)
+ [使用 Rocket Enterprise Server 和 LRS V AWS PSX/MFI 实现大型机批量打印工作负载的现代化](modernize-mainframe-batch-printing-workloads-on-aws-by-using-rocket-enterprise-server-and-lrs-vpsx-mfi.md)
+ [使用 Micro Focus 企业服务器和 LRS VPSX/MFI 在 AWS 上实现大型机在线打印工作负载的现代化](modernize-mainframe-online-printing-workloads-on-aws-by-using-micro-focus-enterprise-server-and-lrs-vpsx-mfi.md)
+ [使用 Rocket Enterprise Server 和 L PageCenter RS X 实现大型机输出管理的现代化 AWS](modernize-mainframe-output-management-on-aws-by-using-rocket-enterprise-server-and-lrs-pagecenterx.md)
+ [使用 Transfer Family 将大型机文件直接移动到 Amazon S3](move-mainframe-files-directly-to-amazon-s3-using-transfer-family.md)
+ [使用 AWS CDK 和 GitHub 操作工作流程优化多账户无服务器部署](optimize-multi-account-serverless-deployments.md)
+ [优化 AWS Blu Age 现代化应用程序的性能](optimize-performance-aws-blu-age-modernized-application.md)
+ [使用 IaC 原则自动 blue/green 部署 Amazon Aurora 全球数据库](p-automate-blue-green-deployments-aurora-global-databases-iac.md)
+ [使用 Precission Connect 将大型机数据库复制到 AWS](replicate-mainframe-databases-to-aws-by-using-precisely-connect.md)
+ [使用 Amazon ECS Anywhere 在亚马逊 WorkSpaces 上运行亚马逊 ECS 任务](run-amazon-ecs-tasks-on-amazon-workspaces-with-amazon-ecs-anywhere.md)
+ [将遥测数据从发送到 AWS Lambda 以 OpenSearch 进行实时分析和可视化](send-telemetry-data-from-lambda-to-opensearch-for-analytics-visualization.md)
+ [在多区域、多账户组织中设置 CloudFormation 偏差检测](set-up-aws-cloudformation-drift-detection-in-a-multi-region-multi-account-organization.md)
+ [使用 AWS Lambda 以六边形架构构建 Python 项目](structure-a-python-project-in-hexagonal-architecture-using-aws-lambda.md)
+ [使用 LocalStack 和 Terraform 测试来测试 AWS 基础架构](test-aws-infra-localstack-terraform.md)
+ [使用自定义语言将 Easytrieve 转换为现代语言 AWS Transform](transform-easytrieve-modern-languages.md)
+ [将 SAP Pacemaker 集群从升级到 ENSA1 ENSA2](upgrade-sap-pacemaker-clusters-from-ensa1-to-ensa2.md)
+ [将 Amazon Q 开发者版用作编码助手来提高工作效率](use-q-developer-as-coding-assistant-to-increase-productivity.md)
+ [在本地验证 Account Factory for Terraform (AFT) 代码](validate-account-factory-for-terraform-aft-code-locally.md)