一、简介
Amazon API Gateway 是一项 AWS 服务,用于创建、发布、维护、监控和保护任意规模的 REST、HTTP 和 WebSocket API。主要解决服务治理,包含缓存、限流、熔断,还有路由、鉴权、聚合等功能。
AWS Lambda 是一项计算服务,可使您无需预配置或管理服务器即可运行代码。AWS Lambda 只在需要时执行您的代码并自动缩放,从每天几个请求到每秒数千个请求。您只需按消耗的计算时间付费 – 代码未运行时不产生费用。熟称“胶水代码”,可以和云上的大部分服务进行组合使用。
二、使用场景
API Gateway 的架构如下图所示,API Gateway 用作应用程序的“前门”,以便访问后端服务的数据、业务逻辑或功能,例如在 AWS Lambda 上运行的代码、任意 Web 应用程序或实时通信应用程序。
对于一个前后端分离的应用来说,如下使用场景非常适合于应用的“后端”:
- 浏览器发起一个后端的请求后
- API Gateway 用作API的“前门”,接收请求并进行路由转发,将请求转发给Lambda
- Lambda作为后台API,可以访问和操纵数据库。
- DynamoDB作为non-sql的数据库,用于存入业务数据。
三、Demo
本节目的:通过AWS CDK创建一个项目,以实现如下服务的部署:
- 创建一个ApiGateway,添加一个路由,它纯用于路由的转发,这里以现成的百度首页为例,将请求访问
https://<ApiGatewayUrl>/baidu
时, 自动跳转到http://baidu.com
页面。 - 创建一个lambda方法,并将其添加到ApiGateway的一个新的路由之中,当用户使用
Http-Method=GET
访问https://<ApiGatewayUrl>/book/1
时,能调到lambda方法。 - 创建一个lambda方法,并将其添加到ApiGateway的一个新的路由之中,当用户使用
Http-Method=POST
访问https://<ApiGatewayUrl>/book
时,能调到lambda方法。
1. 运行命令,创建一个CDK项目并使用vscode打开
mkdir apigateway_lambda-demo && cd apigateway_lambda-demo
cdk init -l typescript && code .
2. 填充stack的代码
在lib/apigateway_lambda-demo-stack.ts
文件里填充如下代码:
import * as cdk from "@aws-cdk/core"; // 必须引入的包
import * as ec2 from "@aws-cdk/aws-ec2"; // for VPC
import * as apigatewayv2 from "@aws-cdk/aws-apigatewayv2"; // 使用api gateway
import * as lambda from "@aws-cdk/aws-lambda"; // 使用lambda
export class ApigatewayLambdaDemoStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 通常VPC已经默认创建好了,这一步是为了寻找现有的VPC
const vpc = ec2.Vpc.fromLookup(this, "Vpc", { isDefault: true });
// 通常ApiGateway可以加两种类型的intergration,一是HttpProxyIntegration,作为代理进行路由转发,二是LambdaProxyIntegration,调用lambda
// 创建第一个Integration,会跳转到百度首页
const baiduHttpProxyIntergration = new apigatewayv2.HttpProxyIntegration({
url: "https://baidu.com",
method: apigatewayv2.HttpMethod.GET,
});
// 创建一个get method的lambda方法
const getBookByIdHandler = new lambda.Function(this, "getBookByIdHandler", {
runtime: lambda.Runtime.NODEJS_12_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async function(event, context){
return {
statusCode: 200,
body: 'your book id is:' + event.pathParameters.book_id
}
}
`),
// code: lambda.Code.fromAsset("./dist-website"), // 将本地文件夹dist-website上传到lambda中,可以是一个.zip文件或文件夹,不可以指定具体文件
});
// 创建一个LambdaProxyIntegration,具体的逻辑是上面的lambda方法
const getBookByIdLambdaIntergation = new apigatewayv2.LambdaProxyIntegration(
{ handler: getBookByIdHandler }
);
// 创建一个post method的lambda方法
const postBookHandler = new lambda.Function(this, "postBookHandler", {
runtime: lambda.Runtime.NODEJS_12_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async function(event, context){
return {
statusCode: 200,
body: 'your book has been added'
}
}
`),
});
// 创建一个LambdaProxyIntegration,具体的逻辑是上面的lambda方法
const postBookHandlerLambdaIntergation = new apigatewayv2.LambdaProxyIntegration(
{ handler: postBookHandler }
);
// 创建一个ApiGateway
const congApiGateway = new apigatewayv2.HttpApi(this, "congApiGateway", {
corsPreflight: {
// 设置跨域
allowHeaders: ["Authorization"], // 允许http header中包含Authorization关键字
allowMethods: [
// 允许发送get/head/options/post请求
apigatewayv2.HttpMethod.GET,
apigatewayv2.HttpMethod.HEAD,
apigatewayv2.HttpMethod.OPTIONS,
apigatewayv2.HttpMethod.POST,
],
allowOrigins: ["*"], // 允许所有的ip都能访问此url
// maxAge: Duration.days(10), // 调用API前会选发送options请求,这里表示10天内可以直接调用API,而不用先发送options请求
},
defaultIntegration: baiduHttpProxyIntergration, // 设置默认的Intergration
});
// 向ApiGateway中添加路由规则,get请求
congApiGateway.addRoutes({
integration: baiduHttpProxyIntergration,
path: "/baidu",
methods: [apigatewayv2.HttpMethod.GET],
});
// 向ApiGateway中添加路由规则,get请求带参数
congApiGateway.addRoutes({
integration: getBookByIdLambdaIntergation,
path: "/book/{book_id}",
methods: [apigatewayv2.HttpMethod.GET],
});
// 向ApiGateway中添加路由规则,post请求
congApiGateway.addRoutes({
integration: postBookHandlerLambdaIntergation,
path: "/book",
methods: [apigatewayv2.HttpMethod.ANY], // 这里直接写成POST后,发起请求的时候不会成功,这里要写成ANY
});
// 发布ApiGateway,分别发布到dev开发环境和prod生产环境,如果不填stageName,默认的环境名为$default,在ApiGateway的URL后面可不用添加环境名。
congApiGateway.addStage("development", {
stageName: "dev",
autoDeploy: true,
});
congApiGateway.addStage("production", {
stageName: "prod",
autoDeploy: true,
});
const apiGatewayUrl = `https://${congApiGateway.httpApiId}.execute-api.${
cdk.Stack.of(this).region
}.${cdk.Stack.of(this).urlSuffix}`;
new cdk.CfnOutput(this, "defaultUrl", { value: `${apiGatewayUrl}/dev` });
new cdk.CfnOutput(this, "baiduUrl", {
value: `${apiGatewayUrl}/dev/baidu`,
});
new cdk.CfnOutput(this, "bookUrl", { value: `${apiGatewayUrl}/dev/book` });
}
}
3. 运行部署命令
cdk deploy
# Outputs:
# ApigatewayLambdaDemoStack.baiduUrl = https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev/baidu
# ApigatewayLambdaDemoStack.bookUrl = https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev/book
# ApigatewayLambdaDemoStack.defaultUrl = https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev
# Stack ARN:
# arn:aws:cloudformation:ap-east-1:067379595472:stack/ApigatewayLambdaDemoStack/e1f457c0-23e2-11eb-a8bf-0a320f0cd704
4. 测试
# 跳转到了百度
/mnt/c/CongStudy/aws-cdk-demo/apigateway_lambda-demo$ curl -v https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev/baidu
* Trying 18.166.18.216...
* Connected to 05y8wk26y4.execute-api.ap-east-1.amazonaws.com (18.166.18.216) port 443 (#0)
* found 148 certificates in /etc/ssl/certs/ca-certificates.crt
* found 592 certificates in /etc/ssl/certs
* ...
> GET /dev/baidu HTTP/1.1
> Host: 05y8wk26y4.execute-api.ap-east-1.amazonaws.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Date: Wed, 11 Nov 2020 06:02:54 GMT
< Content-Type: text/html
< Content-Length: 161
< Connection: keep-alive
< server: bfe/1.0.8.18
< apigw-requestid: V1C2Di_pHUYEPpA=
< location: http://www.baidu.com
< ...
# 发送get请求带参数
/mnt/c/CongStudy/aws-cdk-demo/apigateway_lambda-demo$ curl https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev/book/1
your book id is:1
# 发送post请求
/mnt/c/CongStudy/aws-cdk-demo/apigateway_lambda-demo$ curl -XPOST https://05y8wk26y4.execute-api.ap-east-1.amazonaws.com/dev/book
your book has been added
5. 在console中验证
6. 销毁创建的所有服务以节省成本
cdk destroy