并发调用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
相关产品推荐
相关产品推荐

