You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

并发调用AWS Lambda时返回空Payload问题求助

问题描述

我采用胖Lambda(fat-lambda)方式构建了多个金丝雀(Canary)用于测试Lambda鉴权函数,通过环境变量CANARY_PURPOSE指定触发的测试类型(authorizer-success或authorizer-failure)。核心逻辑是Canary调用目标鉴权Lambda,传入对应Payload并校验响应,但遇到以下异常:

  • 顺序执行时,第二个请求的Lambda响应始终为空Payload
  • 并发调用authorizer-success5次,仅随机2次能获取有效Payload,其余均为空,且所有请求HTTP状态码均为200
  • 被调用的鉴权Lambda日志中明确存在响应Payload,调用方在鉴权Lambda日志生成约10ms后收到空Payload响应

Canary主Handler代码

const { isLambda } = require('../helpers/isLambda');
const { authorizerSuccess } = require('./authorizer-success');
const { authorizerFailure } = require('./authorizer-failure');
const log = require('SyntheticsLogger');

exports.handler = async () => {
  const purpose = process.env.CANARY_PURPOSE;
  log.info('Begin canary execution: ', purpose);
  switch (purpose) {
    case 'authorizer-success':
      await authorizerSuccess();
      break;
    case 'authorizer-failure':
      await authorizerFailure();
      break;
    default:
      throw new Error(`Unknown purpose: ${purpose}`);
  }
  log.info('Canary completed successfully');
};

if (!isLambda) {
  exports.handler();
}

authorizer-success核心代码

const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda');
const { jsonData } = require('../helpers/getVariables');
const log = require('SyntheticsLogger');
const authorizerSuccess = async function () {
  if (!jsonData.authorizer_arn) {
    throw new Error('Authorizer ARN is not defined');
  }
  if (!jsonData.authorizer_payload) {
    throw new Error('Authorizer payload is not defined');
  }

  const invokeParams = {
    FunctionName: jsonData.authorizer_arn,
    Payload: JSON.stringify({ ...jsonData.authorizer_payload }),
    InvocationType: 'RequestResponse',
  };

  const input = new InvokeCommand(invokeParams);
  log.info({ invokeParams });

  const client = new LambdaClient();
  const data = await client.send(input);
  log.info({ data });
  const jsonString = Buffer.from(data.Payload).toString('utf8');

  log.info({ jsonString });
  if (!jsonString || jsonString.length === 0) {
    throw new Error('No response from authorizer');
  }
  const parsedData = JSON.parse(jsonString);

  log.info({ parsedData });
  if (parsedData.statusCode === 403) {
    throw new Error('Login failed, check credentials');
  }
  return 'Authorizer-success canary check successful!';
};

exports.authorizerSuccess = authorizerSuccess;

调用方日志示例

2024-05-22T19:27:15.283Z    aaaa8d-a0af-4248-843e-3aaaa11b  INFO    {
  data: {
    '$metadata': {
      httpStatusCode: 200,
      requestId: '8aaaad-3408-4caf-90e2-31aaaa7a',
      extendedRequestId: undefined,
      cfId: undefined,
      attempts: 1,
      totalRetryDelay: 0
    },
    ExecutedVersion: '$LATEST',
    Payload: Uint8ArrayBlobAdapter(0) [Uint8Array] [],
    StatusCode: 200
  }
}
排查与解决方案

1. 复用LambdaClient实例

当前代码每次调用authorizerSuccess都会新建LambdaClient实例,在并发或多次执行场景下,频繁创建客户端可能导致连接复用异常,进而出现Payload丢失。将客户端实例提升到模块级别复用:

const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda');
const { jsonData } = require('../helpers/getVariables');
const log = require('SyntheticsLogger');

// 模块级别初始化,复用同一个客户端
const client = new LambdaClient();

const authorizerSuccess = async function () {
  // ... 原有校验逻辑
  const data = await client.send(input); // 直接复用已创建的客户端
  // ... 后续处理逻辑
};

exports.authorizerSuccess = authorizerSuccess;

2. 优化Payload处理逻辑

AWS SDK v3返回的Payload是Uint8ArrayBlobAdapter,直接转换可能存在异常,增加校验和重试逻辑:

// 替换原Payload转换代码
let jsonString = '';
// 处理Uint8Array类型的Payload
if (data.Payload instanceof Uint8Array && data.Payload.length > 0) {
  jsonString = Buffer.from(data.Payload).toString('utf8');
} else if (typeof data.Payload === 'string') {
  // 兼容字符串类型的Payload
  jsonString = data.Payload;
}

// 空Payload时重试一次
if (!jsonString) {
  log.warn('Empty payload received, retrying once');
  const retryData = await client.send(input);
  if (retryData.Payload instanceof Uint8Array && retryData.Payload.length > 0) {
    jsonString = Buffer.from(retryData.Payload).toString('utf8');
  }
  if (!jsonString) {
    throw new Error('No response from authorizer after retry');
  }
}

3. 避免执行环境上下文复用导致的数据污染

Lambda执行环境会被复用,若jsonData是模块加载时初始化的,可能出现并发下的数据不一致。改为在函数内部动态获取变量:

// 替换原有的静态引入,改为动态调用
const getVariables = require('../helpers/getVariables');

const authorizerSuccess = async function () {
  // 每次调用时重新获取变量,避免环境复用导致的旧数据
  const jsonData = getVariables(); // 若为异步则用await getVariables()
  // ... 后续校验逻辑
};

4. 检查目标鉴权Lambda的响应输出

确认鉴权Lambda的handler是否正确返回响应:

  • 异步handler需确保return了正确序列化的JSON对象
  • 回调模式需确保callback(null, response)被正确调用,未出现未捕获的异常导致提前退出
  • 排查并发场景下鉴权Lambda是否存在资源竞争,导致响应未正确生成

5. 开启SDK调试日志

在LambdaClient初始化时开启详细日志,排查请求/响应细节:

const client = new LambdaClient({
  // 开启SDK调试日志,需确保SyntheticsLogger支持debug级别
  logger: {
    trace: (msg) => log.debug(msg),
    debug: (msg) => log.debug(msg),
    info: (msg) => log.info(msg),
    warn: (msg) => log.warn(msg),
    error: (msg) => log.error(msg),
  },
  // 增加重试次数,应对偶发的网络异常
  maxAttempts: 3
});

内容的提问来源于stack exchange,提问作者Vallo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:14:50