正在加载,请稍候…

AWS Lambda 最佳实践:冷启动、层与成本优化

编写生产就绪的 Lambda 函数。学习如何最小化冷启动、使用 Lambda 层、正确处理错误、管理密钥以及优化成本和性能。

AWS Lambda 最佳实践:冷启动、层与成本优化

AWS Lambda 最佳实践

冷启动优化

// 错误:在 handler 内部初始化连接
export const handler = async (event: APIGatewayEvent) => {
  const db = await createDatabaseConnection(); // 每次调用都冷启动!
  const result = await db.query('SELECT ...');
  return result;
};

// 正确:在 handler 外部初始化(跨调用复用)
const db = await createDatabaseConnection(); // 每个容器只运行一次

export const handler = async (event: APIGatewayEvent) => {
  const result = await db.query('SELECT ...');
  return result;
};
// 使用适当大小的连接池
// Lambda 的 CPU/内存有限,小池子即可
const pool = new Pool({
  host: process.env.DB_HOST,
  max: 5,         // Lambda 的小连接池
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

AWS Lambda 最佳实践:冷启动、层与成本优化 插图

错误处理

import { APIGatewayProxyHandler } from 'aws-lambda';

export const handler: APIGatewayProxyHandler = async (event) => {
  try {
    const body = JSON.parse(event.body ?? '{}');
    const result = await processRequest(body);

    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(result),
    };
  } catch (err) {
    console.error('Handler error:', { err, event }); // CloudWatch

    if (err instanceof ValidationError) {
      return {
        statusCode: 400,
        body: JSON.stringify({ error: err.message }),
      };
    }

    // 不要暴露内部错误
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Internal server error' }),
    };
  }
};

AWS Lambda 最佳实践:冷启动、层与成本优化 插图

环境变量与密钥

import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';

// 在调用之间缓存密钥
const secretCache = new Map<string, { value: string; expiresAt: number }>();

async function getSecret(name: string): Promise<string> {
  const cached = secretCache.get(name);
  if (cached && cached.expiresAt > Date.now()) {
    return cached.value;
  }

  const ssm = new SSMClient({ region: process.env.AWS_REGION });
  const response = await ssm.send(
    new GetParameterCommand({ Name: name, WithDecryption: true })
  );

  const value = response.Parameter!.Value!;
  secretCache.set(name, {
    value,
    expiresAt: Date.now() + 5 * 60 * 1000, // 缓存 5 分钟
  });

  return value;
}

AWS Lambda 最佳实践:冷启动、层与成本优化 插图

Lambda 层

# serverless.yml
service: my-api

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1

layers:
  dependencies:
    path: layers/dependencies
    name: api-dependencies
    description: Node.js dependencies
    compatibleRuntimes:
      - nodejs20.x

functions:
  api:
    handler: src/handler.handler
    layers:
      - !Ref DependenciesLambdaLayer
    events:
      - http:
          path: /api/{proxy+}
          method: ANY

结构化日志

function createLogger(context: LambdaContext) {
  return {
    info: (message: string, data?: object) => {
      console.log(JSON.stringify({
        level: 'INFO',
        message,
        requestId: context.awsRequestId,
        ...data,
      }));
    },
    error: (message: string, err?: unknown, data?: object) => {
      console.error(JSON.stringify({
        level: 'ERROR',
        message,
        requestId: context.awsRequestId,
        error: err instanceof Error ? { message: err.message, stack: err.stack } : err,
        ...data,
      }));
    },
  };
}

export const handler = async (event: APIGatewayEvent, context: LambdaContext) => {
  const logger = createLogger(context);
  logger.info('Request received', { path: event.path, method: event.httpMethod });
  // ...
};

成本优化

// SAM template with reserved concurrency
// template.yaml
// Resources:
//   ApiFunction:
//     Type: AWS::Serverless::Function
//     Properties:
//       ReservedConcurrentExecutions: 100  # 防止失控成本
//       MemorySize: 256   # 从低开始,分析后调整
//       Timeout: 29       # 小于 API GW 30s 超时

// 使用自定义指标监控
import { CloudWatchClient, PutMetricDataCommand } from '@aws-sdk/client-cloudwatch';

async function recordMetric(name: string, value: number, unit: string): Promise<void> {
  const cloudwatch = new CloudWatchClient({ region: process.env.AWS_REGION });
  await cloudwatch.send(new PutMetricDataCommand({
    Namespace: 'MyApp',
    MetricData: [{
      MetricName: name,
      Value: value,
      Unit: unit,
      Dimensions: [{ Name: 'Environment', Value: process.env.ENVIRONMENT! }],
    }],
  }));
}

Lambda 按毫秒计费,奖励快速、精简的函数——优化启动时间和内存。