【问题标题】:AWS Lambda RDS connection timeoutAWS Lambda RDS 连接超时
【发布时间】:2017-07-25 02:39:42
【问题描述】:

我正在尝试使用连接到我的 RDS 数据库的 Node.js 编写一个 Lambda 函数。该数据库正在运行并且可以从我的 Elastic Beanstalk 环境中访问。当我运行该函数时,它会返回一个超时错误。

尝试将超时时间增加到 5 分钟,结果完全相同。

经过一番研究,我得出的结论是,这可能是一个安全问题,但在亚马逊的文档或this 答案中找不到解决方案(这是我唯一能找到的关于该主题的答案)。

以下是安全细节:

  • RDS 和 Lambda 都在同一个安全组中。
  • RDS 具有所有流量入站和出站规则。
  • Lambda 在其角色中具有 AmazonVPCFullAccess 策略。

我的代码是:

'use strict';
console.log("Loading getContacts function");

var AWS = require('aws-sdk');
var mysql = require('mysql');

exports.handler = (event, context, callback) => {

   var connection = mysql.createConnection({
        host     : '...',
        user     : '...',
        password : '...',
        port     : 3306,
        database: 'ebdb',
        debug    :  false
    });

    connection.connect(function(err) {
      if (err) callback(null, 'error ' +err);
      else callback(null, 'Success');
    });

};

我得到的结果是:

"errorMessage": "2017-03-05T05:57:46.851Z 9ae64c49-0168-11e7-b49a-a1e77ae6f56c Task timed out after 10.00 seconds"

【问题讨论】:

标签: node.js amazon-web-services aws-lambda rds


【解决方案1】:

我正在分享我在连接 RDS 时的经验。

您需要为Lambda function 启用VPC 访问权限,在此期间您将为其分配Security Group

然后,在分配给 RDS 实例的安全组内,您将为分配给 Lambda 函数的安全组启用访问权限。

您可以获取更多信息here

【讨论】:

  • 请阅读问题。您提到的所有内容都已包含在问题中。
  • Lambda 和 RDS 都在同一个(默认)VPC 上
【解决方案2】:

RDS 和 Lambda 都在同一个安全组中。

这是关键。默认情况下,不允许在同一安全组内进行通信。而且您需要明确允许它(E.x sg-xxxxx ALL TCP)。这仅在您的 lambda 尝试通过私有 ip 访问 db 时才有效。

如果它试图通过公共 IP 访问它,它将无法正常工作,您还需要为此打出必要的整体。

但是有更好的方法:

  1. 为您的 lambda 创建单独的安全组
  2. 允许 RDS sg 中端口 3306 上的入站流量用于 lambdas sg。

【讨论】:

  • 为 Lambda 创建了一个单独的安全组,并且所有流量都在 RDS 上入站,但仍然是同样的问题...
  • 救命稻草。谁会想到 AWS 在默认情况下会阻止同一安全组内的通信? AWS 教程都没有提到这一点,他们很清楚您需要将您的 Lambda 和 RDS 在同一个组中,但没有提到您需要使它们能够通信。 (我的首选方法是添加一个入站规则以允许来自同一安全组内的所有 TCP 流量,但是为 Lambda 创建一个新的并启用它的建议当然也可以。)
【解决方案3】:

我要感谢所有提供帮助的人,问题结果与我想象的不同。代码中的 callback 出于某种原因不起作用,即使它位于亚马逊自己的默认示例中。

工作代码如下所示:

'use strict';
console.log("Loading getContacts function");

var AWS = require('aws-sdk');
var mysql = require('mysql');

exports.handler = (event, context) => {

   var connection = mysql.createConnection({
        host     : '...',
        user     : '...',
        password : '...',
        port     : 3306,
        database: 'ebdb',
        debug    :  false
    });

    connection.connect(function(err) {
      if (err) context.fail();
      else context.succeed('Success');
    });

};

【讨论】:

  • 我为此奋斗了一个多小时——将近两个小时。我以为我的防火墙规则被破坏了。 OMG,简单地删除回调线如何解决所有问题?无论如何,好的提示,我做过同样的事情。一定是某种回调死锁什么的。
  • 您需要在调用回调之前结束连接。由于连接保持打开状态,lambda 超时。需要在.connect()connection.end(function (err) { callback(null, response);});的回调中添加这样的东西。
  • 遇到了这个答案 - 只是想指出回调参数是可选的,具体取决于您的 NodeJS 版本,根据 AWS Docs:docs.aws.amazon.com/lambda/latest/dg/…
【解决方案4】:

问题不在于超时,而在于您关闭连接的方式。如果您不想等待回调或在关闭.end(function(err) { //Now call your callback }); 中的连接时正确使用回调,请改用.destroy()

请参阅this thread 以获得更深入的解释。

【讨论】:

    【解决方案5】:

    虽然可以使用上下文,但您只需将context.callbackWaitsForEmptyEventLoop = false; 添加到处理程序,然后像这样正常使用回调:

    exports.handler = (event, context) => {
      context.callbackWaitsForEmptyEventLoop = false; 
      var connection = mysql.createConnection({
        //connection info
      });
      connection.connect(function(err) {
        if (err) callback(err); 
        else callback(null, 'Success');
      });
    };
    

    答案在文档中(我花了几个小时才找到这个): http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-using-old-runtime.html

    在“比较上下文和回调方法”部分中,它有一个“重要”说明来解释事情。

    在注释的底部写着:

    因此,如果您想要与上下文方法相同的行为,则必须将上下文对象属性 callbackWaitsForEmptyEventLoop 设置为 false。

    基本上,回调会持续到事件循环的结束,而不是结束事件循环的上下文。所以设置 callbackWaitsForEmptyEventLoop 使得回调像上下文一样工作。

    【讨论】:

    • 传奇!谢谢,这是正确答案:context.callbackWaitsForEmptyEventLoop = false;
    • 不是所有的英雄都穿斗篷!谢谢。 context.callbackWaitsForEmptyEventLoop = false;是正确的 context.callbackWaitsForEmptyEventLoop = false; const response = { statusCode: 200, body: JSON.stringify({ headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': true, }, message: await mysqlConnector .create('table_name', {test: 50}), 输入:事件, }), };回调(空,响应);
    【解决方案6】:

    我也遇到过类似的超时情况。在connection.connect() 之后,问题不在connection.end() 上。 Connection.end() 应该在callback 之前完成。

    工作代码:

      var mysql = require('mysql');
    
        var connection = mysql.createConnection({
            host     : 'host_name',
            user     : 'root',
            password : 'password'
        });
    
    
        module.exports.handler = (event, context, callback) => {
    
    // **Connection to database**      
    connection.connect(function(err) {
            if (err) {
              console.error('Database connection failed: ' + err.stack);
              return;
            }
            console.log('Connected to database.');
          });
    
        // **Hit DB Query**
          connection.query("Query", function(err, rows, fields) {
               console.log(rows);
            });
    
    
          //**Close Connection**
    
    connection.end(); ***// Missing this section will result in timeout***
    
        //**Send API Response**
          callback(null, {
                  statusCode: '200',
                  body: "Success",
                  headers: {
                      'Content-Type': 'application/json',
                  },
          });
    
        };
    

    【讨论】:

      【解决方案7】:

      当您最初设置数据库时,它会自动创建一个安全组。默认为您设置数据库的 IP。当您从 lambda 运行时,此规则会阻止流量。查看您的数据库错误日志,您可以确认它拒绝连接。

      ***** could not be resolved: Name or service not known
      

      您需要在安全组中创建规则以允许 lambda 流量。转到您的 RDS 实例控制台并单击安全组,选择入站。在那里你会看到规则。然后拨打电话向世界开放,查找 AWS lambda IP 或创建 VPC。

      【讨论】:

      【解决方案8】:

      connection.end() 应该在回调之后:

      这样的工作代码:

          'use strict';
      var mysql = require('mysql');
      
      var connection = mysql.createConnection({
          host     : 'xxxxxx.amazonaws.com',
          user     : 'testuser',
          password : 'testPWD',
          port     : 3306,
          database: 'testDB',
          debug    : false        
      });
      
      module.exports.handler = (event, context, callback) => {
          // **Connection to database**      
          connection.connect(function(err) {
              if (err) {
                  console.error('Database connection failed: ' + err.stack);
                  context.fail();
                  return;
              }
            else{ 
                  console.log('Connected to database.');
              }
          });
      
          connection.query('show tables from testDB', function (error, results, fields) {
              if (error) {
                  console.log("error: connection failed with db!");
                  connection.destroy();
                  throw error;
              } else {
                  // connected!
                  console.log("info: connection ok with db!");
                  console.log(results);
                  context.succeed("done");
                  callback(error, results);
              }
          });
      
          //Send API Response
          callback(null, {
              statusCode: '200',
              body: 'succeed',
              headers: {
                'Content-Type': 'application/json',
              },
          });
      
          //Close Connection
          connection.end(); // Missing this section will result in timeout***
      
      };
      

      【讨论】:

        【解决方案9】:

        我花了大约 2 天的时间才弄清楚确切的问题。在我的情况下,RDS 和 Lambda 函数都在相同的 VPC、子网和安全组中,并添加了所需的角色,但仍然出现 Socket 超时异常。我可以通过以下链接更改入站和出站规则来解决问题 -

        https://aws.amazon.com/premiumsupport/knowledge-center/connect-lambda-to-an-rds-instance/

        【讨论】:

        • 感谢上帝,我已经为此苦苦挣扎了一周
        猜你喜欢
        • 2017-10-05
        • 2017-05-24
        • 2017-12-27
        • 2018-11-16
        • 1970-01-01
        • 1970-01-01
        • 2020-12-26
        • 1970-01-01
        • 2021-02-27
        相关资源
        最近更新 更多