收到太多对MySQL查询的请求时,Lambda函数“错误:连接ETIMEDOUT”

时间:2019-03-28 19:27:42

标签: mysql node.js aws-lambda load-testing serverless

我正在尝试对我的AWS lambda函数进行测试,该函数对RDS MySQL(t2.medium)进行查询。 但是,如果我多次请求该API,尽管我可以使用正确的数据成功查询,但有时会出现“错误:connect ETIMEDOUT”。

我的代码或设置有什么问题吗?

我阅读了一些设置一些参数的建议:

MySQL:

  

wait_timeout 1

     

最大连接数16000

     

interactive_timeout 6000

     

max_allowed_pa​​cket 1073741824

Lamdba:

  

超时60秒将Lambda函数与RDS放在同一VPC中

     

添加了VPC执行策略AWSLambdaVPCAccessExecutionRole

     

将安全组分配给lambda函数

     

在RDS实例附带的安全性中,添加了入站规则   对于mysql

     

确认Lambda函数可以访问相同的VPC RDS数据库

Lambda错误日志

  

2019-03-28T18:51:47.353Z ab4fbbaf-1ea2-458b-a5b5-781cdfdd80df {错误:   连接ETIMEDOUT

     

在Connection._handleConnectTimeout

     

(/ var / task / node_modules / mysql / lib / Connection.js:411:13)

     

在Object.onceWrapper(events.js:313:30)

     

在titterNone(events.js:106:13)

     

在Socket.emit(events.js:208:7)

     

在Socket._onTimeout(net.js:420:8)

     

ontimeout(timers.js:482:11​​)

     

在tryOnTimeout(timers.js:317:5)

     

在Timer.listOnTimeout(timers.js:277:5)

     
     

at Protocol._enqueue   (/var/task/node_modules/mysql/lib/protocol/Protocol.js:144:48)

     

at Protocol.handshake   (/var/task/node_modules/mysql/lib/protocol/Protocol.js:51:23)

     

在Connection.connect   (/var/task/node_modules/mysql/lib/Connection.js:118:18)

     

在Connection._implyConnect   (/var/task/node_modules/mysql/lib/Connection.js:453:10)

     

在Connection.query   (/var/task/node_modules/mysql/lib/Connection.js:198:8)

     

承诺(/var/task/db.js:62:9)

     

在新的Promise()中

     

在Object.retrieve(/var/task/db.js:55:10)

     

在exports.getAlerts(/var/task/index.js:59:24)

     

errorno:“ ETIMEDOUT”,

     

代码:“ ETIMEDOUT”,

     

系统调用:“连接”,

     

致命:true

RDS错误日志

  

2019-03-28T18:18:49.378320Z 9500 [注意] 9500与数据库的连接中断:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.392514Z 9498 [注意]中止了9498与数据库的连接:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.470617Z 9499 [Note]中止了与数据库的连接9499:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.636775Z 9501 [Note]中止了与数据库的连接9501:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.694669Z 9502 [注意]中止了与数据库的连接9502:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.803457Z 9503 [注意]中止了与数据库的连接9503:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

     

2019-03-28T18:18:49.824250Z 9504 [Note]中止了与数据库的连接9504:   'db'用户:'user'主机:'123.123.123.123'(读取超时)   通信数据包)

我在lambda上的db.js查询

const mysql = require('mysql')
let retrieve = (sql, objectArr, entityName) => {
      return new Promise((resolve, reject) => {
        let con = mysql.createConnection(dbParams)
        con.query(sql, objectArr, (err2, results) => {
          con.end()
          if (err2) {
            console.log(err2)
            return reject(new apiError.DatabaseError('An error occurred in retrieve'))
          }
          console.log('Data retrieve successfully')
          return resolve(results)
        })
      })
    }

我的test.js脚本

const request = require('request')
let errorCount = 0
let success = 0

for (let i = 0; i < 4000; i++) {
  console.log('Send')
  request.get('https://myapi/users', {
    headers: {
      'client_id': 'app',
      'Content-Type': 'application/json'
    }
  }, (error, response, body) => {
    if (error) {
      console.log('some error')
      errorCount++
    } else {
      let jsonBody = JSON.parse(body)
      if (jsonBody.code === 0) {
        success++
      } else {
        errorCount++
      }
    }

    console.log('Success: ', success)
    console.log('Error: ', errorCount)
  })
}

编辑: 我还尝试更改测试脚本中的i <1,然后它总是显示“错误:连接ETIMEDOUT。但是对于i <900,它有时会成功工作

index.js

const db = require('./db.js')
exports.getUsers = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false
  try {
    let sql = 'SELECT * FROM User'
    let abc = await db.retrieve(sql, [], 'user')

    let response = {
      statusCode: 200,
      abc: abc,
      code: 0
    }
    return response
  } catch (err) {
    console.log(err)
    errorHandler(err)
  }
}

带泳池的db.js

const mysql = require('mysql')
const constants = require('./constants.js')

let dbParams = {
  host: constants.SQL_CONNECTION_HOST,
  user: constants.SQL_CONNECTION_USER,
  password: constants.SQL_CONNECTION_PASSWORD,
  database: constants.SQL_CONNECTION_DATABASE,
  multipleStatements: true,
  maxConnections: 4
}

const pool = mysql.createPool(dbParams)
let retrieve = (sql, objectArr, entityName) => {
  return new Promise((resolve, reject) => {
    pool.getConnection((err1, con) => {
      if (err1) {
        console.log(err1)
        return reject(new apiError.DatabaseError('An error occurred in retrieve pool'))
      }
      console.log('Pool connect successfully')
      con.query(sql, objectArr, (err2, results) => {
        con.end()
        if (err2) {
          console.log(err2)
          return reject(new apiError.DatabaseError('An error occurred in retrieve'))
        }
        console.log('Data retrieve successfully')
        return resolve(results)
      })
    })
  })
}

使用完池后的错误日志

  

2019-03-28T23:35:24.144Z 91b0fc78-e4d1-4fd9-bdf7-923715b165c0 {错误:   握手不活动超时

     

在握手。   (/var/task/node_modules/mysql/lib/protocol/Protocol.js:163:17)

     

在titterNone(events.js:106:13)

     

在Handshake.emit(events.js:208:7)

     

在握手时。_onTimeout   (/var/task/node_modules/mysql/lib/protocol/sequences/Sequence.js:124:8)

     

在Timer._onTimeout   (/var/task/node_modules/mysql/lib/protocol/Timer.js:32:23)

     

ontimeout(timers.js:482:11​​)

     

在tryOnTimeout(timers.js:317:5)

     

在Timer.listOnTimeout(timers.js:277:5)

     
     

at Protocol._enqueue   (/var/task/node_modules/mysql/lib/protocol/Protocol.js:144:48)

     

at Protocol.handshake   (/var/task/node_modules/mysql/lib/protocol/Protocol.js:51:23)

     

在PoolConnection.connect   (/var/task/node_modules/mysql/lib/Connection.js:118:18)

     

在Pool.getConnection(/var/task/node_modules/mysql/lib/Pool.js:48:16)

     

承诺(/var/task/db.js:72:10)

     

在新的Promise()中

     

在Object.retrieve(/var/task/db.js:67:10)

     

在exports.getAlerts(/var/task/index.js:59:24)

     

代码:“ PROTOCOL_SEQUENCE_TIMEOUT”,

     

致命:是,

     

超时:10000}

立即设置Pool并使用请求循环对其进行测试:

i <100结果=>成功:866,错误:134个请求。

i <10结果=>成功:8,错误:2个请求。

握手不活动超时错误

外部带有con.createConnection的db.js

const mysql = require('mysql')
// initialize dbParams
let con = mysql.createConnection(dbParams)

let retrieve = (sql, objectArr, entityName) => {
  return new Promise((resolve, reject) => {
    con.query(sql, objectArr, (err2, results) => {
      //con.end() commet out connection end here 
      if (err2) {
        console.log(err2)
        return reject(new apiError.DatabaseError('An error occurred in retrieve'))
      }
      console.log('Data retrieve successfully')
      return resolve(results)
    })
  })
}

1 个答案:

答案 0 :(得分:0)

问题没有解释如何在lambda函数中调用retrieve函数。

尽管如此,在测试中,每次迭代似乎都将执行它,这会创建大量连接并可能解释您看到的行为。

我建议通过将代码行let con = mysql.createConnection(dbParams)移到任何函数之外(这样就只会发生单个连接),在lambda初始化时(也称为“ coldstart”)创建连接。

另一个不错的选择是使用connection pooling