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

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,不需要走公网:

  1. 确认你的区域(eu-west-1)支持SES的VPC端点(该区域是支持的)。
  2. 进入VPC控制台 → 「Endpoints」→「Create Endpoint」:
    • 选择「AWS services」,找到com.amazonaws.eu-west-1.email-smtp(SES的服务端点)。
    • 选择Lambda所在的VPC和子网,以及对应的安全组(安全组需要允许入站HTTPS流量)。
  3. 你的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:53:06