Lambda 上的无服务器架构
Lambda 抽象了基础设施,但引入了新的挑战:冷启动、无状态和按请求计费。

基本 Lambda 函数
// handler.js
export const handler = async (event, context) => {
// event: API Gateway, SQS, S3 等
// context: Lambda 运行时信息
context.callbackWaitsForEmptyEventLoop = false; // 对数据库连接很重要!
try {
const { httpMethod, path, body, headers } = event;
const payload = body ? JSON.parse(body) : null;
// 业务逻辑
const result = await processRequest({ method: httpMethod, path, payload });
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(result),
};
} catch (err) {
console.error('Handler error:', err);
return {
statusCode: err.statusCode ?? 500,
body: JSON.stringify({ error: err.message }),
};
}
};
冷启动优化
当 Lambda 创建新的执行环境时会发生冷启动。主要原因和修复方法:
// 1. 将重初始化移到 handler 外部
import { DynamoDB } from '@aws-sdk/client-dynamodb';
// 每次冷启动执行一次,而不是每次调用
const ddb = new DynamoDB({ region: 'us-east-1' });
const tableName = process.env.TABLE_NAME;
// 2. 仅在需要时懒加载模块
let heavyLibrary;
function getHeavyLib() {
if (!heavyLibrary) {
heavyLibrary = require('./heavy-module');
}
return heavyLibrary;
}
// 3. 最小化包大小——避免打包整个 AWS SDK
import { GetItemCommand } from '@aws-sdk/client-dynamodb'; // 可摇树优化的 v3
包大小缩减
// serverless.js / webpack.config.js
import { nodeExternalsPlugin } from 'esbuild-node-externals';
export default {
functions: {
api: {
handler: 'src/handler.handler',
package: {
individually: true,
},
},
},
custom: {
esbuild: {
bundle: true,
minify: true,
target: 'node20',
exclude: ['@aws-sdk/*'], // Lambda 运行时中可用
},
},
};
数据库连接管理
// 在 warm 调用间复用连接池
let pgPool;
async function getPool() {
if (!pgPool) {
const { Pool } = await import('pg');
pgPool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 1, // Lambda:每个容器一个连接
idleTimeoutMillis: 0,
connectionTimeoutMillis: 5000,
});
}
return pgPool;
}
export const handler = async (event) => {
const pool = await getPool();
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users LIMIT 10');
return { statusCode: 200, body: JSON.stringify(result.rows) };
} finally {
client.release();
}
};
预置并发
# serverless.yml
functions:
api:
handler: src/handler.handler
provisionedConcurrency: 5 # 保持 5 个实例 warm
events:
- http:
path: /api/{proxy+}
method: any
Lambda 层
# 共享依赖层
layers:
dependencies:
path: layer
compatibleRuntimes:
- nodejs20.x
functions:
api:
handler: src/handler.handler
layers:
- !Ref DependenciesLambdaLayer
容器镜像 Lambda
FROM public.ecr.aws/lambda/nodejs:20
COPY package*.json ./
RUN npm ci --only=production
COPY src ./src
CMD ["src/handler.handler"]
中间件模式 (Middy)
import middy from '@middy/core';
import httpRouterHandler from '@middy/http-router';
import httpErrorHandler from '@middy/http-error-handler';
import httpJsonBodyParser from '@middy/http-json-body-parser';
import validator from '@middy/validator';
const getUsers = middy()
.use(httpJsonBodyParser())
.use(validator({ eventSchema: getUsersSchema }))
.use(httpErrorHandler())
.handler(async (event) => {
const users = await userService.list(event.queryStringParameters);
return { statusCode: 200, body: JSON.stringify(users) };
});
export const handler = httpRouterHandler([
{ method: 'GET', path: '/users', handler: getUsers },
]);
使用 Lambda Powertools 进行监控
import { Logger } from '@aws-lambda-powertools/logger';
import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
import { Tracer } from '@aws-lambda-powertools/tracer';
const logger = new Logger({ serviceName: 'user-service' });
const metrics = new Metrics({ namespace: 'MyApp' });
const tracer = new Tracer({ serviceName: 'user-service' });
export const handler = async (event) => {
const segment = tracer.getSegment();
const subsegment = segment.addNewSubsegment('processRequest');
try {
logger.info('Processing request', { event });
const result = await processRequest(event);
metrics.addMetric('SuccessfulRequests', MetricUnits.Count, 1);
return result;
} catch (err) {
metrics.addMetric('FailedRequests', MetricUnits.Count, 1);
logger.error('Request failed', { err });
throw err;
} finally {
subsegment.close();
metrics.publishStoredMetrics();
}
};