无服务器应用实战 #
一、架构概述 #
1.1 无服务器架构 #
text
无服务器应用架构:
┌─────────────────────────────────────────────────────────────┐
│ 用户 │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Route53 DNS │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CloudFront CDN │
└───────────────────────────┬─────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ 静态资源(S3) │ │ API Gateway │
│ Web前端 │ │ REST API │
└───────────────────────┘ └───────────┬───────────┘
│
▼
┌───────────────────────┐
│ Lambda函数 │
│ 业务逻辑处理 │
└───────────┬───────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐
│ DynamoDB │ │ S3 │ │ 其他AWS服务 │
│ 数据存储 │ │ 文件存储 │ │ SES/SNS/SQS │
└───────────────────────┘ └───────────────────────┘ └───────────────────────┘
1.2 架构优势 #
text
无服务器优势:
├── 无需管理服务器
├── 自动扩展
├── 按需付费
├── 高可用
├── 快速部署
└── 降低运维成本
二、项目结构 #
2.1 目录结构 #
text
serverless-app/
├── frontend/
│ ├── index.html
│ ├── css/
│ └── js/
├── backend/
│ ├── src/
│ │ ├── handlers/
│ │ │ ├── create-item.js
│ │ │ ├── get-item.js
│ │ │ ├── list-items.js
│ │ │ └── delete-item.js
│ │ └── utils/
│ │ └── response.js
│ └── package.json
├── infrastructure/
│ └── template.yaml
└── deploy.sh
三、DynamoDB设计 #
3.1 表设计 #
javascript
const params = {
TableName: 'Items',
KeySchema: [
{ AttributeName: 'PK', KeyType: 'HASH' },
{ AttributeName: 'SK', KeyType: 'RANGE' }
],
AttributeDefinitions: [
{ AttributeName: 'PK', AttributeType: 'S' },
{ AttributeName: 'SK', AttributeType: 'S' },
{ AttributeName: 'GSI1PK', AttributeType: 'S' },
{ AttributeName: 'GSI1SK', AttributeType: 'S' }
],
GlobalSecondaryIndexes: [{
IndexName: 'GSI1',
KeySchema: [
{ AttributeName: 'GSI1PK', KeyType: 'HASH' },
{ AttributeName: 'GSI1SK', KeyType: 'RANGE' }
],
Projection: { ProjectionType: 'ALL' }
}],
BillingMode: 'PAY_PER_REQUEST'
};
3.2 数据模型 #
javascript
const item = {
PK: 'ITEM#item-123',
SK: 'METADATA',
GSI1PK: 'USER#user-456',
GSI1SK: 'ITEM#2024-01-01T00:00:00Z',
itemId: 'item-123',
userId: 'user-456',
title: 'My Item',
description: 'Item description',
status: 'active',
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z'
};
四、Lambda函数 #
4.1 创建项目 #
javascript
const dynamodb = new (require('aws-sdk')).DynamoDB.DocumentClient();
const tableName = process.env.TABLE_NAME;
export const createItem = async (event) => {
const body = JSON.parse(event.body);
const itemId = generateId();
const timestamp = new Date().toISOString();
const item = {
PK: `ITEM#${itemId}`,
SK: 'METADATA',
GSI1PK: `USER#${body.userId}`,
GSI1SK: `ITEM#${timestamp}`,
itemId,
userId: body.userId,
title: body.title,
description: body.description,
status: 'active',
createdAt: timestamp,
updatedAt: timestamp
};
await dynamodb.put({
TableName: tableName,
Item: item
}).promise();
return {
statusCode: 201,
body: JSON.stringify(item)
};
};
export const getItem = async (event) => {
const itemId = event.pathParameters.itemId;
const result = await dynamodb.get({
TableName: tableName,
Key: {
PK: `ITEM#${itemId}`,
SK: 'METADATA'
}
}).promise();
if (!result.Item) {
return {
statusCode: 404,
body: JSON.stringify({ message: 'Item not found' })
};
}
return {
statusCode: 200,
body: JSON.stringify(result.Item)
};
};
export const listItems = async (event) => {
const userId = event.queryStringParameters?.userId;
const result = await dynamodb.query({
TableName: tableName,
IndexName: 'GSI1',
KeyConditionExpression: 'GSI1PK = :pk',
ExpressionAttributeValues: {
':pk': `USER#${userId}`
}
}).promise();
return {
statusCode: 200,
body: JSON.stringify(result.Items)
};
};
export const deleteItem = async (event) => {
const itemId = event.pathParameters.itemId;
await dynamodb.delete({
TableName: tableName,
Key: {
PK: `ITEM#${itemId}`,
SK: 'METADATA'
}
}).promise();
return {
statusCode: 204,
body: ''
};
};
五、API Gateway配置 #
5.1 SAM模板 #
yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Serverless API
Parameters:
TableName:
Type: String
Default: Items
Globals:
Function:
Runtime: nodejs20.x
Handler: index.handler
Environment:
Variables:
TABLE_NAME: !Ref TableName
IAMRoleStatements:
- Effect: Allow
Action:
- dynamodb:PutItem
- dynamodb:GetItem
- dynamodb:Query
- dynamodb:DeleteItem
Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${TableName}'
Resources:
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref TableName
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
- AttributeName: SK
AttributeType: S
- AttributeName: GSI1PK
AttributeType: S
- AttributeName: GSI1SK
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH
- AttributeName: SK
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: GSI1
KeySchema:
- AttributeName: GSI1PK
KeyType: HASH
- AttributeName: GSI1SK
KeyType: RANGE
Projection:
ProjectionType: ALL
BillingMode: PAY_PER_REQUEST
CreateItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: backend/src/handlers/
Handler: create-item.createItem
Events:
CreateItem:
Type: Api
Properties:
Path: /items
Method: post
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: backend/src/handlers/
Handler: get-item.getItem
Events:
GetItem:
Type: Api
Properties:
Path: /items/{itemId}
Method: get
ListItemsFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: backend/src/handlers/
Handler: list-items.listItems
Events:
ListItems:
Type: Api
Properties:
Path: /items
Method: get
DeleteItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: backend/src/handlers/
Handler: delete-item.deleteItem
Events:
DeleteItem:
Type: Api
Properties:
Path: /items/{itemId}
Method: delete
Outputs:
ApiEndpoint:
Description: API Gateway endpoint URL
Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/'
六、前端配置 #
6.1 S3静态网站 #
html
<!DOCTYPE html>
<html>
<head>
<title>Serverless App</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<h1>Items List</h1>
<form id="createForm">
<input type="text" id="title" placeholder="Title" required>
<textarea id="description" placeholder="Description"></textarea>
<button type="submit">Create Item</button>
</form>
<ul id="itemsList"></ul>
</div>
<script>
const API_URL = 'https://your-api-id.execute-api.region.amazonaws.com/Prod';
async function loadItems() {
const response = await axios.get(`${API_URL}/items?userId=user-123`);
const items = response.data;
const list = document.getElementById('itemsList');
list.innerHTML = items.map(item => `
<li>
<h3>${item.title}</h3>
<p>${item.description}</p>
<button onclick="deleteItem('${item.itemId}')">Delete</button>
</li>
`).join('');
}
document.getElementById('createForm').addEventListener('submit', async (e) => {
e.preventDefault();
const title = document.getElementById('title').value;
const description = document.getElementById('description').value;
await axios.post(`${API_URL}/items`, {
userId: 'user-123',
title,
description
});
loadItems();
});
async function deleteItem(itemId) {
await axios.delete(`${API_URL}/items/${itemId}`);
loadItems();
}
loadItems();
</script>
</body>
</html>
七、部署流程 #
7.1 构建和部署 #
bash
sam build
sam deploy --guided \
--stack-name serverless-app \
--capabilities CAPABILITY_IAM
7.2 部署前端 #
bash
aws s3 sync frontend/ s3://your-bucket-name/ --delete
aws s3 website s3://your-bucket-name/ \
--index-document index.html \
--error-document error.html
八、监控配置 #
8.1 CloudWatch告警 #
yaml
HighErrorRate:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Lambda error rate high
MetricName: Errors
Namespace: AWS/Lambda
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 5
ComparisonOperator: GreaterThanThreshold
8.2 X-Ray追踪 #
yaml
Globals:
Function:
Tracing: Active
九、安全配置 #
9.1 API Gateway认证 #
yaml
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoAuthorizer
Type: COGNITO_USER_POOLS
IdentitySource: method.request.header.Authorization
RestApiId: !Ref ServerlessRestApi
ProviderARNs:
- !GetAtt UserPool.Arn
9.2 CORS配置 #
yaml
ApiGatewayCors:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref ServerlessRestApi
ResourceId: !Ref ItemsResource
HttpMethod: OPTIONS
AuthorizationType: NONE
Integration:
Type: MOCK
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization'"
method.response.header.Access-Control-Allow-Methods: "'GET,POST,DELETE'"
method.response.header.Access-Control-Allow-Origin: "'*'"
RequestTemplates:
application/json: '{"statusCode": 200}'
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
十、成本优化 #
10.1 成本监控 #
yaml
Budget:
Type: AWS::Budgets::Budget
Properties:
Budget:
BudgetName: ServerlessAppBudget
BudgetLimit:
Amount: 50
Unit: USD
TimeUnit: MONTHLY
BudgetType: COST
NotificationsWithSubscribers:
- Notification:
NotificationType: ACTUAL
ComparisonOperator: GREATER_THAN
Threshold: 80
Subscribers:
- SubscriptionType: EMAIL
Address: admin@example.com
十一、小结 #
本章介绍了无服务器应用实战:
| 内容 | 要点 |
|---|---|
| 架构设计 | API Gateway + Lambda + DynamoDB |
| DynamoDB | 表设计、GSI索引 |
| Lambda | 函数编写、权限配置 |
| API Gateway | REST API、认证、CORS |
| 部署 | SAM模板、自动化部署 |
下一步学习 #
完成无服务器应用后,接下来可以:
最后更新:2026-03-28