架构总览
最近在学习一套基于 AWS 的服务端架构,整体分为四层:
| 层级 |
组件 |
| 边缘 & 网关层 |
Lambda@Edge、AWS AppSync |
| 负载均衡层 |
Lambda 负载均衡(ALB) |
| 业务服务层 |
Stat Task、Account / Order / Management / Assets / Image Handler Service |
| 监控层 |
Amazon CloudWatch |
Lambda 是 Serverless
AWS Lambda 是 Serverless(无服务器)架构的典型实现,也就是 FaaS(Function as a Service)。
核心思想:你只写代码,不管服务器。
- 被调用时自动启动容器运行代码,用完自动销毁
- 流量突增时自动扩容,流量下去自动缩容到零
- 按实际调用次数和运行时长收费,没有请求就是零费用
类比出租车:Lambda 就像出租车,用的时候叫,用完即走,不需要养一辆车停在车库里持续花费。
Lambda@Edge:边缘执行的 Lambda
Lambda@Edge 是把 Lambda 函数”搬”到 CloudFront 全球边缘节点上运行的能力。普通 Lambda 部署在某个 AWS Region,而 Lambda@Edge 自动复制到离用户最近的 PoP 节点执行,延迟可以从几百毫秒降到几十毫秒。
四个触发点
一个请求从用户到源站,经过 CloudFront 时有四个可以插入逻辑的位置:
- Viewer Request:用户请求刚到 PoP,缓存检查之前。适合 JWT 鉴权、A/B 测试分流、请求路径标准化。
- Origin Request:缓存未命中、即将回源前。适合动态改变源站地址(灰度发布)、注入内部鉴权头。
- Origin Response:源站响应返回后,写入缓存之前。适合修改响应头、替换源站 4xx 为自定义错误页。
- Viewer Response:响应最终返回用户前。适合注入安全响应头(
Strict-Transport-Security、X-Frame-Options)、打点日志。
与普通 Lambda 的区别
Lambda@Edge 有更严格的限制:内存最大 128 MB(普通 Lambda 最高 10 GB),Viewer 触发点执行时间最多 5 秒,Origin 触发点最多 30 秒。代码必须部署在 us-east-1,AWS 自动向全球复制。不支持环境变量。
Lambda 冷启动
什么是冷启动
Lambda 函数平时”不存在”,第一次被调用时 AWS 需要临时”造”出运行环境,这个过程就是冷启动,分为四步:
- 从 S3 下载代码包
- 启动容器(分配 CPU / 内存)
- 初始化运行时(启动 Node.js / JVM 等)
- 执行 handler 外的初始化代码(DB 连接、SDK 初始化等)
前三步是 AWS 控制的 Init 阶段,用户无法干预。第四步是用户代码,可以优化。
冷启动耗时
- Node.js / Python:典型 100~500ms
- Java / Kotlin:因 JVM 启动慢,常见 2~5 秒
热启动(容器复用)时跳过全部 Init 阶段,直接执行业务代码,典型耗时 5ms 左右。
优化方案
1. Provisioned Concurrency(预置并发) — 最彻底的方案
提前告诉 AWS 保持 N 个容器随时热着,这些实例永远不走冷启动流程。代价是按保留实例数量持续收费,适合核心链路。
2. Warming(定时预热)
用 EventBridge 每 5 分钟触发一次 Lambda,防止容器因闲置被销毁。成本极低,但只能保证单实例热着,突发并发时新实例仍然冷启动。
3. 缩小代码包体积
代码包越小,下载越快。Node.js 用 esbuild 做 tree-shaking,能把包从几十 MB 压到几 MB,冷启动时间可缩短 30~50%。
最重要的代码层面优化:初始化代码放 handler 外
1 2 3 4 5 6
| const db = new Database(process.env.DB_URL);
exports.handler = async (event) => { return db.query('SELECT ...'); };
|
1 2 3 4 5
| exports.handler = async (event) => { const db = new Database(process.env.DB_URL); return db.query('SELECT ...'); };
|
handler 外的代码只在冷启动时执行一次,之后容器复用时直接跳过。
Provisioned Concurrency 配置方式
预置并发必须绑定在别名(Alias)或版本(Version)上,不能直接绑在 $LATEST。
方式一:AWS CLI
1 2 3 4 5 6 7 8 9 10 11 12 13
| aws lambda publish-version --function-name order-service
aws lambda put-provisioned-concurrency-config \ --function-name order-service \ --qualifier 1 \ --provisioned-concurrent-executions 3
aws lambda get-provisioned-concurrency-config \ --function-name order-service \ --qualifier 1
|
方式二:Application Auto Scaling(生产推荐)
按时段自动调整预置数量,白天多、深夜少,避免浪费。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| aws application-autoscaling register-scalable-target \ --service-namespace lambda \ --resource-id function:order-service:prod \ --scalable-dimension lambda:function:ProvisionedConcurrency \ --min-capacity 2 \ --max-capacity 20
aws application-autoscaling put-scaling-policy \ --service-namespace lambda \ --resource-id function:order-service:prod \ --scalable-dimension lambda:function:ProvisionedConcurrency \ --policy-name order-service-scaling \ --policy-type TargetTrackingScaling \ --target-tracking-scaling-policy-configuration '{ "TargetValue": 0.7, "PredefinedMetricSpecification": { "PredefinedMetricType": "LambdaProvisionedConcurrencyUtilization" } }'
|
ALB 负载均衡
ALB(Application Load Balancer)把 HTTP 请求按路径规则分发到对应的 Lambda 函数。
三个核心概念:
- Listener(监听器):监听某个端口(如 HTTPS 443)
- Rule(规则):按路径匹配决定转发给谁
- Target Group(目标组):承接请求的后端,每个 Lambda 函数对应一个 Target Group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| resource "aws_lb_target_group" "order_service" { name = "order-service-tg" target_type = "lambda" }
resource "aws_lb_target_group_attachment" "order_service" { target_group_arn = aws_lb_target_group.order_service.arn target_id = aws_lambda_function.order_service.arn }
resource "aws_lb_listener_rule" "order_service" { listener_arn = aws_lb_listener.https.arn priority = 20
condition { path_pattern { values = ["/api/orders/*"] } }
action { type = "forward" target_group_arn = aws_lb_target_group.order_service.arn } }
|
Lambda 接收的 event 结构
ALB 把 HTTP 请求转成 event 对象传给 Lambda:
1 2 3 4 5 6 7 8 9 10 11 12
| exports.handler = async (event) => { const method = event.httpMethod; const path = event.path; const headers = event.headers; const body = JSON.parse(event.body);
return { statusCode: 200, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ orderId: "123", status: "ok" }) }; };
|
Amazon CloudWatch 监控
Lambda 自动向 CloudWatch 上报日志和指标,无需额外配置。需要手动配置的是告警阈值和通知渠道。
五个核心监控指标
| 指标 |
含义 |
告警建议 |
Errors |
函数报错次数 |
> 0 立即告警 |
Duration |
执行时长 |
超过 Timeout 的 80% 时告警 |
Throttles |
被限流次数 |
> 0 告警,说明并发超限 |
ConcurrentExecutions |
并发执行数 |
评估预置并发是否够用 |
InitDuration |
冷启动耗时 |
优化冷启动的核心依据 |
告警配置示例(CDK Python)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from aws_cdk import aws_cloudwatch as cw from aws_cdk import aws_cloudwatch_actions as cw_actions from aws_cdk import aws_sns as sns
topic = sns.Topic(self, "AlertTopic") sns.Subscription(self, "EmailSub", topic=topic, protocol=sns.SubscriptionProtocol.EMAIL, endpoint="your-team@example.com" )
error_alarm = cw.Alarm(self, "OrderServiceErrors", metric=order_lambda.metric_errors(), threshold=1, evaluation_periods=1, alarm_description="Order Service Lambda 报错", treat_missing_data=cw.TreatMissingData.NOT_BREACHING ) error_alarm.add_alarm_action(cw_actions.SnsAction(topic))
|
Logs Insights 查询示例
1 2 3 4 5 6 7 8 9 10 11
| fields @timestamp, @duration, @initDuration | filter @type = "REPORT" and ispresent(@initDuration) | sort @initDuration desc | limit 20
fields @timestamp, @message | filter @message like /ERROR/ | sort @timestamp desc | limit 50
|
总结
这套 AWS 架构的核心设计思路是:用 Serverless 彻底免除服务器运维负担,用 Lambda@Edge 把计算推到离用户最近的地方,用 ALB 把请求按业务职责分散到独立的函数,用 CloudWatch 统一收口监控和告警。
各服务的预置并发策略建议:
Account Service、Order Service:配置 Provisioned Concurrency(建议从 2~3 个开始),用户直接感知延迟
Management Service、Assets Service、Image Handler:按需冷启动即可,延迟容忍度较高
Stat Task:定时任务,完全不需要预置并发