Node.js Lambda查询MySQL并调用SES发送邮件时超时问题求助
解决Node.js Lambda同时执行MySQL查询和SES发送邮件超时问题
从你的日志和配置来看,问题的核心很明确:部署在VPC中的Lambda无法访问SES的API端点,导致ses.sendEmail()的请求一直挂起,最终触发超时。
问题根源分析
你的Lambda在VPC内运行,而AWS SES是托管的公网服务(即使和Lambda在同一区域)。VPC内的Lambda默认没有公共IP,无法直接访问互联网——这就是为什么单独跑MySQL(VPC内服务)没问题,单独跑SES如果是在非VPC环境下测试也能正常运行,但两者结合(或拆分Lambda后)就超时的原因。日志里执行到===SENDING EMAIL===后就没了下文,说明sendEmail的回调根本没触发,请求卡在网络层面了。
解决方案
方案1:为VPC配置NAT网关(通用方案)
让Lambda所在的私有子网通过NAT网关访问公网,这样就能连接SES的API:
- 在你的VPC的公有子网中创建一个NAT网关,绑定弹性IP(EIP)。
- 编辑Lambda所在私有子网的路由表,添加一条路由规则:
0.0.0.0/0→ 指向刚创建的NAT网关。 - 确认Lambda的安全组允许**出站HTTPS(443端口)**流量到任意地址(SES用HTTPS通信)。
方案2:使用SES的VPC端点(推荐,无需公网)
AWS支持为SES创建VPC接口端点,让Lambda在VPC内部直接访问SES,不需要走公网:
- 确认你的区域(eu-west-1)支持SES的VPC端点(该区域是支持的)。
- 进入VPC控制台 → 「Endpoints」→「Create Endpoint」:
- 选择「AWS services」,找到
com.amazonaws.eu-west-1.email-smtp(SES的服务端点)。 - 选择Lambda所在的VPC和子网,以及对应的安全组(安全组需要允许入站HTTPS流量)。
- 选择「AWS services」,找到
- 你的IAM角色已经包含
SES:SendEmail权限,无需额外调整。
代码优化建议
除了网络问题,你的代码可以做些优化,避免异步嵌套带来的潜在问题:
改用Promise+async/await的写法,代码更清晰,也更容易调试:
'use strict'; const aws = require('aws-sdk'); const mysql = require('mysql'); const ses = new aws.SES({ region: 'eu-west-1' }); // 包装MySQL连接为Promise const connectDB = (connection) => { return new Promise((resolve, reject) => { connection.connect(err => err ? reject(err) : resolve()); }); }; // 包装MySQL查询为Promise const queryDB = (connection, sql) => { return new Promise((resolve, reject) => { connection.query(sql, (err, rows) => { err ? reject(err) : resolve(rows); }); }); }; module.exports.helloWorld = async (event, context) => { // 关闭事件循环等待,减少Lambda不必要的超时等待 context.callbackWaitsForEmptyEventLoop = false; let connection; try { console.log("Hit here 1"); connection = mysql.createConnection({ host: "xxx", user: "xxx", password: "xxx", database: "xxx" }); await connectDB(connection); console.log("Hit here 2"); const rows = await queryDB(connection, `SELECT * FROM User U WHERE id = 1`); console.log("Hit here 3"); console.log(rows); const emailParams = { Destination: { ToAddresses: [rows[0].email] }, Message: { Body: { Html: { Data: "Test" }, Text: { Data: "Test" } }, Subject: { Data: "Test Email" } }, Source: "team@hello.com" }; console.log("Hit here 4"); // 先关闭数据库连接 connection.end(); console.log('===SENDING EMAIL==='); // 使用AWS SDK的Promise方式发送邮件 const emailResult = await ses.sendEmail(emailParams).promise(); console.log("===EMAIL SENT==="); console.log(emailResult); return { statusCode: 200, body: JSON.stringify({ message: "Email sent successfully" }) }; } catch (error) { console.error('Error:', error); // 出错时确保关闭数据库连接 if (connection) connection.end(); return { statusCode: 500, body: JSON.stringify({ error: error.message }) }; } };
另外,记得开启context.callbackWaitsForEmptyEventLoop = false,这能避免Lambda等待事件循环为空才结束,减少不必要的等待时间。
关于拆分Lambda的补充说明
你之前拆分SES逻辑到另一个Lambda仍超时,是因为被调用的Lambda同样部署在VPC中,也无法访问SES的公网端点。解决上述网络问题后,拆分Lambda的方案也能正常工作。
内容的提问来源于stack exchange,提问作者Karthik
相关产品推荐
相关产品推荐

