mysql - 收到太多 MySQL 查询请求时,Lambda 函数“错误:连接 ETIMEDOUT”
问题描述
我正在尝试对我的 AWS lambda 函数进行测试,该函数对 RDS MySQL (t2.medium) 进行查询。但是,如果我多次请求 API,虽然我可以使用正确的数据成功查询,但有时会导致“错误:连接 ETIMEDOUT”。
我的代码或设置有什么问题吗?
我阅读了一些设置一些参数的建议:
MySQL:
等待超时 1
最大连接数 16000
交互超时 6000
max_allowed_packet 1073741824
拉姆巴:
超时 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)
在 emitNone (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)
在 Protocol._enqueue (/var/task/node_modules/mysql/lib/protocol/Protocol.js:144:48)
在 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)
在新的承诺 ()
在 Object.retrieve (/var/task/db.js:55:10)
在exports.getAlerts (/var/task/index.js:59:24)
错误号:'ETIMEDOUT',
代码:'ETIMEDOUT',
系统调用:'连接',
致命的:真的}
RDS 错误日志
2019-03-28T18:18:49.378320Z 9500 [注意]中止连接 9500 到 db:'db' user:'user' host:'123.123.123.123'(读取通信数据包超时)
2019-03-28T18:18:49.392514Z 9498 [Note] Aborted connection 9498 to db: 'db' user: 'user' host: '123.123.123.123' (读取通信数据包超时)
2019-03-28T18:18:49.470617Z 9499 [注意]中止连接 9499 到 db:'db' user:'user' host:'123.123.123.123'(读取通信数据包超时)
2019-03-28T18:18:49.636775Z 9501 [注意]中止连接 9501 到 db:'db' user:'user' host:'123.123.123.123'(读取通信数据包超时)
2019-03-28T18:18:49.694669Z 9502 [Note] Aborted connection 9502 to db: 'db' user: 'user' host: '123.123.123.123' (读取通信数据包超时)
2019-03-28T18:18:49.803457Z 9503 [Note] Aborted connection 9503 to db: 'db' user: 'user' host: '123.123.123.123' (读取通信数据包超时)
2019-03-28T18:18:49.824250Z 9504 [Note] Aborted connection 9504 to db: 'db' user: 'user' host: '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)
在 emitNone (events.js:106:13)
在 Handshake.emit (events.js:208:7)
在 Handshake._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)
在 Protocol._enqueue (/var/task/node_modules/mysql/lib/protocol/Protocol.js:144:48)
在 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)
在新的承诺 ()
在 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 个请求。
握手不活动超时错误
db.js 与 con.createConnection 外部
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)
})
})
}
解决方案
问题中没有解释如何retrieve
在您的 lambda 函数中调用该函数。
尽管如此,它似乎会在您的测试中的每次迭代中执行,这会创建大量的连接并可能解释您看到的行为。
我建议在 lambda 初始化时(又名“冷启动”)创建连接,方法是将代码行移到let con = mysql.createConnection(dbParams)
任何函数之外(这样只会发生一个连接)。
另一个不错的选择是使用连接池。
推荐阅读
- java - 在早期的 Android 中显示本周的 7 个日期
- azure - 使用 azure 数据工厂将 sftp 从远程服务器增量到 azure 的方法是什么
- regex - 正则表达式匹配字符串末尾不需要的字符
- android - 牛轧糖获得 LOCKED_BOOT_COMPLETED 动作
- ms-access-2013 - 需要访问查询以反映具有最新日期/跟进字段的数据
- javascript - 如何使用 jQuery 添加事件监听器?
- c# - 实现自己的 KeyValuePair 版本的问题
- python - 将excel数据转换为嵌套字典和列表
- javascript - Access-Control-Allow-Origin 不是 * 但 Chrome 坚持是(升级 apollo-server 后)
- c++ - OpenGL:在着色器中执行 TexCoord 计算,不好的做法?