使用 Amazon API Gateway 和 Amazon Lambda 实现请求-响应
若完成后及时清除资源(说明如下),则本教程免费
完成的示例可从 GitHub 获取
前提条件
在开始本教程之前,需要满足以下条件:
- 亚马逊云科技账户:如何创建免费的亚马逊云科技账户
- Amazon Cloud Development Kit (Amazon CDK):如何设置并引导 Amazon CDK
简介
请求-响应模式是现代 Web 和云架构中广泛应用的通信模型。它有助于在客户端和服务器之间同步交互,在需要时提供实时响应和即时反馈。
该模式简单易懂,是世界上最大的分布式系统(即互联网)的基础。每当您在浏览器中打开网页或点击链接时,浏览器都会向服务器发送请求,服务器则返回 HTML 文档作为响应。
在本分步教程中,我们将使用 Amazon API Gateway 和 Amazon Lambda 函数来实现此模式。
我们还会重点介绍 Amazon Cloud Development Kit (CDK) 将整个基础设施定义为代码的强大功能。
若想了解有关 Amazon CDK 的更多信息,请参阅 Amazon CDK 开发人员指南。
完成本教程后,您将充分了解基于 HTTP 的请求-响应 API 的各组成部分,在 Amazon API Gateway 和 Amazon Lambda 之间实现同步通信,并获得使用 CDK 构建基础设施即代码的一些实践经验。
但是,在我们开始构建 API 之前,先大致了解一下同步请求-响应模式的优缺点。
同步请求-响应模式的优缺点
优点
- 实时交互:同步请求-响应模式支持向客户端提供即时反馈,进而增强实时交互并改善用户体验。
- 简易性:请求-响应模式简单易懂,可轻松上手,易于实现,尤其对于刚开始构建 API 的开发人员来说尤为适合。
- 错误处理:同步通信简化了错误和异常处理,可及时通知客户端。
缺点
- 可扩展性:在处理大量并发请求时,同步模式可能会影响应用程序的可扩展性,进而成为瓶颈。
- 延迟增加:客户端必须等收到响应后才能继续,这就产生了延迟,特别是在处理时间较长的情况下,延迟就更严重。
- 故障漏洞:下游服务或网络发生中断或出现故障时,可能会导致响应延迟或失败。这会影响系统的整体可靠性和可用性。
在做出架构决策时,务必要权衡利弊,并选择最符合您特定需求和约束条件的通信模式。许多现代应用程序依赖于多种集成模式,包括同步请求-响应以及异步点对点消息传递和基于事件的通信。
但现在,我们先开始学习本教程,了解如何使用 Amazon API Gateway 和 Amazon Lambda 实现同步请求-响应模式。
关于编码时的资源费用说明:本教程仅使用极少量的资源,且所有这些资源都包含在亚马逊云科技免费套餐内,从每个账户的创建之日起 12 个月内可免费试用:
- 几千字节的代码将存储在 Amazon S3 中,Amazon S3 可提供 5 GB 的免费存储空间。
- 我们将调用几次 API Gateway,每月可免费调用 100 万次。
- 我们将运行几次 Lambda 函数,同样每月可免费调用 100 万次。
因此,如果您按照本分步指南操作,肯定不会超出免费套餐限额。我也在本教程末尾增加了一部分,帮助您删除本教程中创建的所有资源。
步骤 1 - 创建并部署 CDK 应用程序
1.1 初始化 CDK 应用程序
打开您选择的终端(个人喜欢使用 VS Code 中的集成终端),然后为项目创建一个新目录并前往该目录:
mkdir request-response-example
cd request-response-example
使用以下命令初始化新的 CDK 应用程序。在本例中,我们将使用 TypeScript。
cdk init app --language=typescript
此操作将创建具有空堆栈的 CDK 应用程序。
查看文件结构,其中包含所需的基架和配置,以及下面两个 TypeScript 文件:
- CDK 应用程序:bin/request-response/example.ts
- 空堆栈定义:lib/request-response-example-stack.ts,这就是我们今天要使用的文件。
1.2 部署空堆栈
在添加任何新资源之前,我们可以先部署空堆栈,确保一切都已设置好。
为此,输入以下命令:
cdk deploy
此命令会将应用程序“合成”到 Amazon CloudFormation 模板,并将其部署到您的亚马逊云科技账户中。
如果 cdk deploy 引发错误,请确保您已按照 CDK 设置指南中的说明引导您的亚马逊云科技账户。
部署完成后,我们前往亚马逊云科技管理控制台上的 Amazon CloudFormation 控制面板,查看新的 CloudFormation 堆栈。
如果找不到该堆栈,请确保您选择的区域正确无误,若要切换区域,可点击导航栏中当前显示的区域名称,然后选择正确的区域。所选区域必须与您在配置 Amazon CLI 时使用的区域保持一致。
现在打开并浏览堆栈详细信息。在 Resources(资源)页签中,您会看到 CDKMetadata 已部署,其中包含堆栈的一些配置详情。
步骤 2 - 创建并部署 API
一切都设置好后,您就可以创建第一个真正的资源了。
我们来创建一个 API Gateway,它只是简单地将任何请求重定向到现有的 URL。
专业提示:从单体应用程序迁移到使用微服务和/或无服务器函数的应用程序时,通常会首先使用此方法。在客户端和服务器之间部署 API Gateway 会创建一个抽象层,这样可逐步迁移各个端点,而无需在每次更改后修改和重新部署客户端。您可以简单地将每个路由重定向到旧应用程序,然后再将各个路由逐个切换到新创建的服务。
首先,在编辑器中打开 lib/request-response-example-stack.ts。
删除一些介绍性注释后,代码应如下所示:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class RequestResponseExampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Add your resources here
}
}
注意:如果您遇到困难或出现问题,可前往本文末尾查看 lib/request-response-example-stack.ts 的完整源代码。随时可供参考。
2.1 创建简单的 API
我们先在文件顶部添加一条额外的 import 语句:
import * as apigw from 'aws-cdk-lib/aws-apigateway';
现在可以在 constructor 中创建 REST API 资源:
const api = new apigw.RestApi(this, 'SimpleApi', {
restApiName: 'Simple API'
});
Amazon API Gateway 使用集成定义 API 的各个路由的行为。为了实现重定向(更具体地说,是重定向到 URL 的代理),CDK 提供了一个预配置的 HttpIntegration,它可以使用目标 URL 完成初始化:
const redirect = new apigw.HttpIntegration('https://aws.amazon.com')
若要将集成应用于 API,我们需要将集成与路由和 HTTP 方法相关联。在本例中,我们要将 GET 方法添加到 API 的 root 路径,并将其与 redirect 集成相关联。
api.root.addMethod('GET', redirect);
完成此配置后,对该阶段根路径(如 https://your-invoke-url/prod/)的请求将重定向到 https://aws.amazon.com。
2.2 部署 API
若要部署 API,请从项目目录运行以下终端命令:
cdk deploy
您现在可以在终端输出中监控各个资源的状态,一段时间后,部署应该会完成。
2.3 检查部署的 API
前往亚马逊云科技管理控制台中的 API Gateway 控制面板,然后查看 API 列表。如果找不到您的资源,请确保区域正确无误。
点击 API 名称打开详细信息页面,其中包含根资源 / 及其 GET 方法。
点击 GET 打开方法详细信息页面,然后查看 Integration Request(集成请求):
我们堆栈中的 new HttpIntegration('https://aws.amazon.com') 语句实现了 HTTP_PROXY 类型的集成部署,可将 GET 请求发送到指定的 URL:
2.4 测试 API
现在一切都部署好了,下面我们手动测试 API。
点击左侧导航菜单中的 Stages(阶段)。
在此页面上,您应该能够看到“prod”阶段。
API Gateway 阶段:在亚马逊云科技上,API 分阶段部署,例如 dev、test 和 prod,分别表示各自的环境,或者分版本部署,例如 v1、v2 等,方便进行版本控制。如果您没有定义阶段,Amazon CDK 的 RestApi 构造将默认创建名为 prod 的阶段。
点击 prod 将显示该阶段的详细信息,包括我们最感兴趣的内容:Invoke URL(调用 URL)。
此 URL 是为该 API 自动创建的。当然,在实际场景中,您可以在配置环境时使用更清晰易读的自定义域。
现在点击该链接,如果一切配置正确,浏览器将立即重定向到目标 URL。
API 现已设置完成,下面我们创建一个简单的 Amazon Lambda 函数。
步骤 3 - 创建 Lambda 函数
3.1 编写 Lambda 函数源代码
我们将 Lambda 函数代码存储在 CDK 应用程序内的一个资产文件夹中,这样 CDK 就可以自动打包源代码并将其上传到新的 Lambda 函数。
确保您位于项目目录(而不是 bin/ 或 lib/)下,然后创建以下目录以及文件本身:
mkdir lambda-src
mkdir lambda-src/backend-function
touch lambda-src/backend-function/index.js
在编辑器中打开新创建的 lambda-src/test-function/index.js,然后输入以下代码:
exports.handler = async function (event, context) {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda')
};
};
这是一个非常简单的 Lambda 函数处理程序,将返回包含 HTTP 状态码和正文的 JSON 对象。
3.2 将 Lambda 函数添加到堆栈
若要在亚马逊云科技上创建实际的 Lambda 函数,您需要将其添加到堆栈。为此,我们返回 lib/request-response-example-stack.ts。
首先,在文件顶部再添加一条 import 语句:
import * as lambda from 'aws-cdk-lib/aws-lambda';
然后,在构造函数中 const api = ... 上方添加以下代码段:
const backendFunction = new lambda.Function(
this, 'BackendFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('lambda-src/backend-function'),
handler: 'index.handler'
});
这将创建一个 Lambda 函数,该函数使用 Node.js 运行时,打包并上传我们之前创建的资产目录 (lambda-src/backend-function) 中的所有文件,将环境配置为使用 index 文件 (index.js) 的 handler 函数。
3.3 在亚马逊云科技上部署函数
若要部署该函数及其源代码,请在项目目录中运行以下命令:
cdk deploy
Lambda 函数需要一些基本 IAM 权限,默认情况下通过基本执行角色提供这些权限。但是,如果对堆栈的任何更改需要创建或修改 IAM 权限,CDK 将提示您再次进行检查以确保安全。
在以下截图中,您可以看到 CDK 想要部署两项影响 IAM 权限的更改:
- 允许 Lambda 服务 (lambda.amazon.aws) 担任附加到 Lambda 函数的 IAM 角色。
- 将 AWSLambdaBasicExecutioRole 权限添加到该 IAM 角色。
输入 y 进行确认,CDK 将部署新的 Lambda 函数及其 IAM 角色。
Lambda 权限:与亚马逊云科技中的所有其他资源一样,Lambda 函数在默认情况下没有任何权限,也就是说,它不能访问亚马逊云科技中的任何其他资源。如果 Lambda 函数要使用基本功能(需要将日志发送到 Amazon CloudWatch 的权限),则以上步骤必不可少。
3.4测试 Lambda 函数
函数部署后,前往 Amazon Lambda 控制面板中的函数列表。
函数名称已自动生成,包含堆栈名称、函数名称和一个随机后缀,因此应以 BackendResponseExample... 开头。
点击函数名称打开其详细信息,然后向下滚动到 Code source(代码源)部分。
左侧文件资源管理器中的文件夹包含一个名为 index.js 的文件。
双击文件名打开其源代码,您可以看到它与我们在资产文件夹中创建的文件完全一样。
下面,我们配置一个测试事件。
为此,点击 Test(测试)按钮。如果您还没有配置测试事件,此操作将打开测试配置页面。输入 Event name(事件名称),如 test-event,然后点击 Save(保存)。
配置测试事件之后,再次点击 Test(测试),此操作现在会直接调用 Lambda 函数。
此外,系统还会创建 Execution results(执行结果)页签,其中包括测试事件名称、响应和一些附加信息。
查看响应,其中应当包含 Lambda 函数返回的 JSON 对象:
{
"statusCode": 200,
"body": "\"Hello from Lambda\""
}
恭喜您!您已经成功部署并测试了 Lambda 函数。
现在 API 和 Lambda 函数都能正常运行,下面我们将这两项连接起来,这样 API 请求将自动转发到函数。
步骤 4 - 将 Lambda 函数连接到 API
4.1 创建新资源并将其连接到 Lambda 函数
首先,在堆栈定义文件中,添加一个新资源:
const backendResource = api.root.addResource('backend')
您现在可以连接该资源,这次使用 LambdaItegration:
backendResource.addMethod(
'GET', new apigw.LambdaIntegration(backendFunction)
);
4.2 部署所有资源
再次运行 cdk deploy:
cdk deploy
LambdaIntegration 将自动创建所有必要的资源,包括 API 调用函数的权限。也就是说,CDK 将自动要求您再次进行检查,只是为了确保所有权限更改都满足预期。
输入 y 进行确认,CDK 将部署这些更改以及一些将其整合在一起的额外资源。
4.3 浏览并测试新 API
在亚马逊云科技管理控制台中浏览部署时,您会发现新的 /backend 路由及其 GET 方法。
点击 GET 检查方法执行情况。其中有一个 LAMBDA_PROXY 类型的新 Integration Request(集成请求),指向我们在步骤 3 中创建的后端函数:
点击 TEST(测试),访问集成测试配置,保留所有默认值,向下滚动,然后点击 Test(测试)按钮。
此操作将向 API 端点发送请求,如果一切设置正确,则响应将包括 HTTP 状态码 200 和正文,正文中包含函数文本:"Hello from Lambda"。
若要在浏览器中测试端点,请在左侧菜单中选择 Stages(阶段),展开 prod 阶段,然后点击 /backend GET 方法以显示新端点的 Invoke URL(调用 URL):
在新的浏览器窗口中打开该 URL 并检查响应,该响应也应该是 "Hello from Lambda"。
大功告成!
恭喜您!您创建了使用 Amazon API Gateway 设置 REST API 所需的一切资源,该 API 可在特定端点上触发 Lambda 函数,并将所有其他请求重定向到预定义的 URL。
您还可以使用 API Gateway 实现其他目标,例如身份验证、节流、API 密钥等。如果您想了解更多信息,请查看 Amazon API Gateway 开发人员指南。
清理资源
您随时都可以继续体验该 API。完成后,您可以运行以下命令删除部署的所有资源:
cdk destroy
经过确认后,系统将删除作为堆栈一部分而创建的所有资源。
唯一剩下的资源将是 Lambda 函数的源代码,该代码已上传到 Amazon S3 中的资产存储桶中。
您可以直接在亚马逊云科技管理控制台的 S3 控制面板中删除此代码:
查找名为 cdk-XYZ-assets-XYZ-your-region 的 S3 存储桶。这是使用 Amazon CDK 引导账户时创建的存储桶。
我建议保留该存储桶,用于进一步体验 Amazon CDK。不必担心费用问题,空的 S3 存储桶完全免费。
您可以选择存储桶名称旁边的单选按钮并点击 Empty(清空),删除该存储桶中的所有内容:
此操作将打开一个确认对话框。再次检查是否为资产存储桶,在文本框中输入 permanently delete,然后点击 Empty(清空)。
总结
同步请求-响应模式是现代 Web 和云架构中广泛使用的通信模型,希望通过学习本教程,您能够清楚地了解如何使用 Amazon API Gateway 和 Amazon Lambda 实现此模式。
如果您想了解更多信息,请查看更多带有 API Gateway 标签的亚马逊云科技社区文章、Amazon API Gateway 开发人员指南以及 Serverless Land 上许多有用的 API Gateway 资源。
完整源代码
以下是 CDK 堆栈 (lib/request-response-example-stack.ts) 的最终源代码:
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as apigw from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class RequestResponseExampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const backendFunction = new lambda.Function(
this, 'BackendFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('lambda-src/backend-function'),
handler: 'index.handler'
});
const api = new apigw.RestApi(this, 'SimpleApi', {
restApiName: 'Simple API',
})
const redirect = new apigw.HttpIntegration('https://aws.amazon.com')
api.root.addMethod('GET', redirect);
const backendResource = api.root.addResource('backend')
backendResource.addMethod(
'GET', new apigw.LambdaIntegration(backendFunction)
);
}
}
以下是 Lambda 函数 (lambda-src/backend-function/index.js) 的源代码:
exports.handler = async function (event, context) {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda')
};
};
GitHub 存储库
此示例的代码也可从 GitHub 存储库中获取,包括本教程中概述的各个步骤的分支。若要进行克隆,只需执行以下代码:
git clone https://github.com/build-on-aws/request-response-with-amazon-api-gateway.git