AWS Lambda RDS 连接超时

Posted

技术标签:

【中文标题】AWS Lambda RDS 连接超时【英文标题】:AWS Lambda RDS connection timeout 【发布时间】: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"

【问题讨论】:

您是否为 Lambda 函数启用了 VPC 访问? 是的,Lambda 和 RDS 都在同一个(默认)VPC 上 notebookbft.wordpress.com/2018/01/09/… 【参考方案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 访问它,它将无法正常工作,您还需要为此打出必要的整体。

但是有更好的方法:

    为您的 lambda 创建单独的安全组 允许 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。

【讨论】:

您的回答解决了我的问题,您知道如何找到我的 AWS Lambda IP 吗? @toonsend 我不认为它是这样工作的。您需要设置 VPC。 docs.aws.amazon.com/lambda/latest/dg/vpc.html【参考方案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/

【讨论】:

感谢上帝,我已经为此苦苦挣扎了一周

以上是关于AWS Lambda RDS 连接超时的主要内容,如果未能解决你的问题,请参考以下文章

AWS Lambda 和 RDS 之间的间歇性超时

AWS Lambda NodeJS 连接到 RDS Postgres 数据库

连接节点 v5.10.1 时握手不活动超时错误。到 aws mysql RDS

AWS RDS / EC2:TimeoutError:Knex:获取连接超时。游泳池可能已满

AWS Lambda:Redis ElastiCache 连接超时错误

在将 aws lambda 与 redis 连接时,任务在 23.02 秒后超时错误