使用 AWS Lambda 构建有界微服务
1. 响应构建与结构体定义
当添加记录时,我们分两步构建响应。首先创建响应体,然后用额外值包装它。这是因为我们将使用 API Gateway 通过 S3 共享的外部应用程序调用 Lambda。
以下是我们需要的结构体:
-
Unicorn 结构体
:包含我们要乘坐的生物信息。
#[derive(Clone, Serialize)]
#[serde(rename_all = "PascalCase")]
struct Unicorn {
name: String,
color: String,
gender: String,
}
impl Unicorn {
fn new(name: &str, color: &str, gender: &str) -> Self {
Unicorn {
name: name.to_owned(),
color: color.to_owned(),
gender: gender.to_owned(),
}
}
}
- Location 结构体 :表示地图上的一个点,由应用程序的 UI 设置。
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Location {
latitude: f64,
longitude: f64,
}
- Request 结构体 :包含请求体和请求上下文,用于获取 Cognito 提供的用户名。
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Request {
body: String,
request_context: RequestContext,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct RequestContext {
authorizer: Authorizer,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Authorizer {
claims: HashMap<String, String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct RequestBody {
pickup_location: Location,
}
- Response 结构体 :用于 API Gateway,包含状态码和头部信息。
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Response {
body: String,
status_code: u16,
headers: HashMap<String, String>,
}
#[derive(Serialize)]
#[serde(rename_all = "PascalCase")]
struct ResponseBody {
ride_id: String,
unicorn: Unicorn,
unicorn_name: String,
eta: String,
rider: String,
}
2. 功能函数
- find_unicorn 函数 :从三个预定义的 Unicorn 值中选择一个。
fn find_unicorn(location: &Location) -> Unicorn {
debug!("Finding unicorn for {}, {}", location.latitude, location.longitude);
let unicorns = [
Unicorn::new("Bucephalus", "Golden", "Male"),
Unicorn::new("Shadowfax", "White", "Male"),
Unicorn::new("Rocinante", "Yellow", "Female"),
];
let mut rng = thread_rng();
unicorns.iter().choose(&mut rng).cloned().unwrap()
}
- record_ride 函数 :构建 DynamoDB 的 put 请求。
fn record_ride(
conn: &DynamoDbClient,
ride_id: &str,
username: &str,
unicorn: &Unicorn,
) -> Result<PutItemOutput, PutItemError> {
let mut item: HashMap<String, AttributeValue> = HashMap::new();
item.insert("RideId".into(), s_attr(ride_id));
item.insert("User".into(), s_attr(username));
item.insert("UnicornName".into(), s_attr(&unicorn.name));
let timestamp = Utc::now().to_string();
item.insert("RequestTime".into(), s_attr(×tamp));
item.insert("Unicorn".into(), unicorn_map(unicorn));
let put = PutItemInput {
table_name: "Rides".into(),
item,
..Default::default()
};
conn.put_item(put).sync()
}
- s_attr 函数 :将可表示为字符串引用的类型转换为 AttributeValue。
fn s_attr<T: AsRef<str>>(s: T) -> AttributeValue {
AttributeValue {
s: Some(s.as_ref().to_owned()),
..Default::default()
}
}
- unicorn_map 函数 :将 Unicorn 的字段转换为映射。
fn unicorn_map(unicorn: &Unicorn) -> AttributeValue {
let mut item = HashMap::new();
item.insert("Name".into(), s_attr(&unicorn.name));
item.insert("Color".into(), s_attr(&unicorn.color));
item.insert("Gender".into(), s_attr(&unicorn.gender));
AttributeValue {
m: Some(item),
..Default::default()
}
}
3. 配置
使用 Serverless Framework 的
serverless.yml
配置文件将 Lambda 部署到 AWS。配置如下:
service: rust-sls
provider:
name: aws
runtime: rust
memorySize: 128
package:
individually: true
plugins:
- serverless-rust
- serverless-finch
functions:
lambda_1:
handler: lambda_1
role: RustSlsLambdaRole
events:
- http:
path: ride
method: post
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: RustSlsApiGatewayAuthorizer
lambda_2:
handler: lambda_2
events:
- http:
path: check
method: get
4. 资源声明
在配置文件中添加资源部分,声明必要的资源和输出变量。
resources:
Resources:
RustSlsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: rust-sls-aws
WebsiteConfiguration:
IndexDocument: index.html
RustSlsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: "RustSlsBucket"
PolicyDocument:
Statement:
-
Effect: "Allow"
Principal: "*"
Action:
- "s3:GetObject"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "RustSlsBucket"
- "/*"
RustSlsCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: RustSls
UsernameAttributes:
- email
AutoVerifiedAttributes:
- email
RustSlsCognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: RustSlsWebApp
GenerateSecret: false
UserPoolId:
Ref: "RustSlsCognitoUserPool"
RustSlsDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Rides
AttributeDefinitions:
- AttributeName: RideId
AttributeType: S
KeySchema:
- AttributeName: RideId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
RustSlsLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: RustSlsLambda
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: DynamoDBWriteAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: Allow
Action:
- dynamodb:PutItem
Resource:
'Fn::GetAtt': [ RustSlsDynamoDBTable, Arn ]
RustSlsApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: RustSls
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: [ RustSlsCognitoUserPool, Arn ]
IdentitySource: method.request.header.Authorization
Outputs:
RustSlsBucketURL:
Description: "RustSls Bucket Website URL"
Value:
"Fn::GetAtt": [ RustSlsBucket, WebsiteURL ]
RustSlsCognitoUserPoolId:
Description: "RustSls Cognito User Pool ID"
Value:
Ref: "RustSlsCognitoUserPool"
RustSlsCognitoUserPoolClientId:
Description: "RustSls Cognito User Pool Client ID"
Value:
Ref: "RustSlsCognitoUserPoolClient"
RustSlsDynamoDbARN:
Description: "RustSls DynamoDB ARN"
Value:
"Fn::GetAtt": [ RustSlsDynamoDBTable, Arn ]
custom:
client:
bucketName: rust-sls-aws
distributionFolder: assets
5. 部署
5.1 权限设置
要部署此应用程序,需要配置 AWS CLI 工具,并创建一个具有以下权限的用户:
- AWSLambdaFullAccess
- IAMFullAccess
- AmazonDynamoDBFullAccess
- AmazonAPIGatewayAdministrator
- AmazonCognitoPowerUser
- CloudFormationAdministrator
其中,CloudFormationAdministrator 权限需要手动创建,可通过添加以下 JSON 定义到策略中:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1449904348000",
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:CreateChangeSet",
"cloudformation:ListStacks",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackResource",
"cloudformation:DescribeStackEvents",
"cloudformation:ValidateTemplate",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet"
],
"Resource": [
"*"
]
}
]
}
5.2 部署脚本
创建一个 bash 脚本,添加必要的部署函数。
extract() {
echo "$DATA" | grep $1 | cut -d " " -f2
}
deploy() {
echo "ASSETS DOWNLOADING"
curl -L https://api.github.com/repos/aws-samples/aws-serverless-workshops/tarball \
| tar xz --directory assets --wildcards "*/WebApplication/1_StaticWebHosting/website" --strip-components=4
echo "LAMBDAS BUILDING"
sls deploy
echo "ASSETS UPLOADING"
sls client deploy
echo "CONFIGURATION UPLOADING"
DATA=`sls info -v`
POOL_ID=`extract PoolId`
POOL_CLIENT_ID=`extract PoolClientId`
REGION=`extract region`
ENDPOINT=`extract ServiceEndpoint`
CONFIG="
window._config = {
cognito: {
userPoolId: '$POOL_ID',
userPoolClientId: '$POOL_CLIENT_ID',
region: '$REGION'
},
api: {
invokeUrl: '$ENDPOINT'
}
};
"
echo "$CONFIG" | aws s3 cp - s3://rust-sls-aws/js/config.js
INDEX=`extract BucketURL`
echo "INDEX: $INDEX"
}
6. 运行与测试
如果从 GitHub 下载了项目源代码,可以使用
deploy.sh
脚本调用
deploy
函数。
./deploy.sh deploy
部署完成后,会输出应用程序的访问链接。在浏览器中打开该链接,即可看到前端 Wild Rydes 应用程序。用户点击 “GIDDY UP!” 按钮并使用 Cognito 注册账户,点击地图并设置取货点,即可看到独角兽头像移动到指定位置。
7. 更新与移除
-
更新
:可以在部署脚本中添加
update函数,使用sls deploy命令更新资源。
update() {
sls deploy
}
-
移除
:使用
remove函数移除部署的资源。
remove() {
echo "ASSETS REMOVING"
sls client remove
echo "LAMBDAS REMOVING"
sls remove
echo "ASSETS CLEANUP"
rm -rf assets
mkdir assets
touch assets/.gitkeep
}
总结
通过本文,我们学习了使用 AWS Lambda 实现有界微服务的方法,包括响应构建、结构体定义、功能函数编写、配置文件设置、资源声明、部署、测试以及更新和移除操作。这种无服务器架构提供了一种直接处理传入请求的方式,并且可以将应用程序轻松移植到 Rust 语言中。
其他推荐书籍
- 《Hands-On Concurrency with Rust》 - Brian L. Troutwine,ISBN: 9781788399975
- 《Rust Quick Start Guide》 - Daniel Arbuckle,ISBN: 9781789616705
建议在实验结束后清理所有资源,避免产生不必要的费用。同时,欢迎在购买书籍的网站上留下评价,分享您的阅读体验。
使用 AWS Lambda 构建有界微服务
8. 部署流程总结
整个部署过程可以用以下流程图表示:
graph TD;
A[创建具有必要权限的用户] --> B[下载前端资源];
B --> C[构建并部署 Lambda];
C --> D[上传资产到 S3];
D --> E[获取配置信息并上传配置文件];
E --> F[测试应用程序];
以下是部署步骤的详细总结表格:
| 步骤 | 操作 | 命令/说明 |
| ---- | ---- | ---- |
| 1 | 创建用户 | 创建具有 AWSLambdaFullAccess、IAMFullAccess 等权限的用户,并手动添加 CloudFormationAdministrator 权限的 JSON 定义 |
| 2 | 下载前端资源 |
curl -L https://api.github.com/repos/aws-samples/aws-serverless-workshops/tarball \| tar xz --directory assets --wildcards "*/WebApplication/1_StaticWebHosting/website" --strip-components=4
|
| 3 | 构建并部署 Lambda |
sls deploy
|
| 4 | 上传资产到 S3 |
sls client deploy
|
| 5 | 获取配置信息并上传配置文件 | 使用
sls info -v
获取信息,生成配置文件并上传到 S3:
echo "$CONFIG" \| aws s3 cp - s3://rust-sls-aws/js/config.js
|
| 6 | 测试应用程序 | 在浏览器中打开提供的 URL,进行注册和操作测试 |
9. 测试过程分析
测试应用程序时,具体的操作步骤如下:
1. 打开部署脚本输出的链接,进入前端 Wild Rydes 应用程序页面。
2. 点击 “GIDDY UP!” 按钮,使用 Cognito 注册账户。在后台,Cognito 会处理用户注册,用户无需直接与该服务交互。
3. 注册成功后,点击地图上的某个点,然后点击 “Set Pickup” 按钮。此时,会触发我们用 Rust 编写的 Lambda 函数。
4. Lambda 函数会随机选择一只独角兽,并将相关信息存储到 DynamoDB 中。我们可以通过 AWS Console 进行验证:
- 在 User Pools 部分的 Users and groups 页面,可以看到注册的用户。
- 在 Lambda 的 Monitoring 选项卡中,打开 CloudWatch 日志,可以查看 Lambda 生成的用户名和存储的位置信息。
- 在 DynamoDB 的 Tables 页面,可以找到 Lambda 存储的记录,点击记录可以查看所有填充的字段。
10. 代码结构与功能梳理
以下是代码中各个结构体和函数的功能梳理表格:
| 结构体/函数 | 功能 |
| ---- | ---- |
| Unicorn | 表示独角兽,包含名称、颜色和性别信息,有构造函数
new
|
| Location | 表示地图上的一个点,包含纬度和经度信息 |
| Request | 包含请求体和请求上下文,用于获取 Cognito 提供的用户名 |
| RequestContext | 运行时填充的映射,解析为结构体 |
| Authorizer | 包含 claims 值,用于获取
cognito:username
|
| RequestBody | 包含取货点的位置信息 |
| Response | 用于 API Gateway,包含状态码和头部信息 |
| ResponseBody | 表示响应体,包含骑行 ID、独角兽信息等 |
| find_unicorn | 从三个预定义的独角兽中随机选择一个 |
| record_ride | 构建 DynamoDB 的 put 请求,将骑行信息存储到数据库中 |
| s_attr | 将可表示为字符串引用的类型转换为 AttributeValue |
| unicorn_map | 将 Unicorn 的字段转换为映射 |
11. 注意事项
-
S3 桶名称
:每个 S3 桶需要一个唯一的全局名称,在配置文件中使用的
rust-sls-aws名称需要替换为实际可用的名称。 -
资源清理
:实验结束后,建议使用
remove函数清理所有部署的资源,避免 AWS 产生不必要的费用。
12. 总结与展望
通过本文的学习,我们掌握了使用 AWS Lambda 构建有界微服务的完整流程,包括从代码编写、配置设置到部署和测试的各个环节。这种无服务器架构结合 Rust 语言,为我们提供了一种高效、安全的开发方式。
在未来的开发中,我们可以进一步优化代码,提高 Lambda 函数的性能和响应速度。例如,可以对
find_unicorn
函数进行优化,根据不同的条件选择更合适的独角兽,而不是简单的随机选择。同时,还可以扩展应用程序的功能,添加更多的 API 接口和业务逻辑,以满足不同的需求。
其他推荐书籍回顾
- 《Hands-On Concurrency with Rust》 - Brian L. Troutwine,ISBN: 9781788399975
- 《Rust Quick Start Guide》 - Daniel Arbuckle,ISBN: 9781789616705
希望这些书籍能帮助你进一步深入学习 Rust 语言和相关的开发技术。同时,也欢迎你在购买书籍的网站上留下评价,与其他读者分享你的阅读体验。
超级会员免费看
741

被折叠的 条评论
为什么被折叠?



