在测试一些代码时,我遇到了连接池问题。 我期望我的lambda函数可能在几秒钟内被调用数千次,并且很难找出连接到数据库的最佳方法。 这是节点/ postgres的一个非常相似的问题:本质上,问题是如果一个连接可用,我需要能够从池中获得连接,但是由于AWS(不可靠)重用的方式,我不能依赖于现有的池lambda容器。
基本上,我正在寻找一种可靠地获取或创建与数据库的连接的方法。 我还没有找到任何示例(例如while(!availableConnections) { tryToGetConnection() }
。我需要与node-pool
进行交互吗?如何使用Knex?
仅当连接来自同一节点进程时,Knex池才有效。
如果AWS Lambda实例未运行共享节点进程,则只需在每个Lambda实例中创建具有最小/最大连接数为1的新池,并希望您的数据库具有足够好的设置以允许数百个同时连接(在RDS中取决于实例大小) )。
阅读此内容后https://forums.aws.amazon.com/thread.jspa?threadID=216000
看起来lambda确实在共享一些进程,因此,如果您能确定哪个是创建的lambda容器的最大数量,则应该能够计算出最佳的池大小(每个容器都有各自的池,因此与DB的总连接为池。 max *最大容器数)。
无论如何,不需要从池中进行手动连接请求,knex自动等待连接,并且如果一切都在几秒钟内结束,则在该时间内不会触发任何超时。
如果您从数据库中收到错误消息,说已达到最大连接数,则需要减小最大池大小。
抱歉,我的句子很中间,我的孩子就像在地板上扔了3升水:1st_place_medal:我稍后会更新上面的评论...
谢谢回复。 听起来好像估计最大池大小将是我最好的选择,即使连接数随时间变化很大。
需要明确的是,没有办法在Knex中关闭连接,对吗? 只有破坏连接池的能力吗? Lambda有时会重复使用容器的事实使一切都丢了。
销毁连接池也会销毁所有连接(优雅地等待它们首先完成),我想当lambda销毁容器时,当进程终止时,其所有打开的TCP套接字都将隐式关闭。
我不明白为什么每个请求后都应该尝试显式关闭连接,因为这会破坏池化的好处。 通过创建大小为1的池并随后销毁它,您将获得相同的效果。
您还可以为池配置空闲超时,如果不使用连接,而只是在池中等待操作,它将自动关闭连接。
我可以使用Knex将COPY查询发送到RedShift集群,而不等待结果吗?
一旦到达Lambda函数的结尾,使用pg Pool进行此操作即可终止查询。
@BardiaAfshin如果在到达lambda函数结束时,lambda容器被销毁并释放了所有套接字,则在这种情况下,db查询也会死掉并且可能无法完成。
我不确定在读取结果值之前由于未完成隐式事务而将COPY查询回滚的情况下,PostgreSQL对客户端连接结束的反应如何...
无论如何,是否可以发送查询都不取决于knex,但这取决于aws lambda和postgresql的工作方式。
我的观察是该查询在RedShift上被杀死,并被回滚。
无论如何,不需要从池中进行手动连接请求,knex自动等待连接,并且如果一切都在几秒钟内结束,则在该时间内不会触发任何超时。
我正在运行一个数据库负载测试脚本,这似乎不是事实。 超过30个并发连接的任何内容都会立即超时,而不是等待打开的连接。
查询完成后,是否可以手动释放当前使用的连接?
有没有人可以进入AWS Lambda并与我们分享这些示例代码? 我希望有人可以共享knex / postgres / lambda的模式和/或反模式。
这是我现在正在使用的-我敢肯定它可以改进,但是希望能证明我是否在正确的道路上……
'use strict';
var pg = require('pg');
function initKnex(){
return require('knex')({
client: 'pg',
connection: { ...details... }
});
}
module.exports.hello = (event, context) =>
{
var knex = initKnex();
// Should I be returning knex here or in the final catch?
knex
.select('*')
.from('my_table')
.then(function (rows) {
context.succeed('Succeeded: ' + JSON.stringify(rows || []));
})
.catch(function (error) {
context.fail(error);
})
.then(function(){
// is destroy overkill? - is there an option for knex.client.release, etc?
knex.destroy();
})
}
我在同一条船上-尽管经过大量的搜寻和测试,但仍未弄清楚最好的方法是什么。 我现在正在做的是:
const dbConfig = require('./db');
const knex = require('knex')(dbConfig);
exports.handler = function (event, context, callback) {
...
connection = {..., pool: { min: 1, max: 1 },
这样,连接(每个容器最多1个)将保持活动状态,因此可以轻松地重用该容器。 最后,我不会破坏我的联系。
http://blog.rowanudell.com/database-connections-in-lambda/
不知道这是否是最好的方法,但是到目前为止,它对我还是有用的。
/耸肩
@austingayler
我不确定在声明const knex
时会发生什么-这是在初始连接处建立的吗? 有人可以澄清吗? (我假设您的dbConfig中有连接信息)
在您的代码中,难道每次调用处理程序时,连接本身都不会被覆盖并重新创建吗?
只是在同一个船上同一个人聊天,我没有太多运气试图弄清楚容器与池的大小(根据
const knex = require('knex');
const client = knex(dbConfig);
client(tableName).select('*')
.then((result) => {
return Promise.all([
result,
client.destroy(),
])
})
.then(([ result ]) => {
return result;
});
TL; DR:只需在调用回调之前设置
context.callbackWaitsForEmptyEventLoop = false
。
AWS Lambda等待空事件循环(默认情况下)。 因此,即使执行回调,函数也可能引发Timeout错误。
请查看以下链接以获取详细信息:
https://github.com/apex/apex/commit/1fe6e91a46e76c2d5c77877be9ce0c206e9ef9fb
致@elhigu @tgriesser :这不是一个麻烦的问题。 这绝对是Lambda环境的问题。 我认为将此问题标记为问题,应该很好解决:)
@mooyoul是的,绝对不是knex问题,但也许是文档问题...尽管我认为我不希望将任何AWS Lambda特定内容用于knex文档,所以请关闭。
请看这个链接
https://stackoverflow.com/questions/49347210/why-aws-lambda-keeps-timing-out-when-using-knex-js
您需要关闭数据库连接,否则Lambda会运行直到超时。
有没有人发现一种将Lambda与Knex结合使用的完美模式?
正确关闭连接后还有其他问题吗?
我要避免的事情是关闭连接,并使其在Lambda调用中可用。
您确定Lambda允许维持通话之间的状态吗?
查看https://scalegrid.io/blog/how-to-use-mongodb-connection-pooling-on-aws-lambda/的node.js部分,它提出了一种解决非空事件循环的方法。
对我来说,它可以在我的本地计算机上运行,但在部署后却无法运行。 我有点被误导了。
事实证明,我的Lambda函数未打开RDS入站源。 在Stack Overflow找到解决方案:将RDS入站源更改为0.0.0.0/0
或使用VPC。
更新RDS入站源之后,我可以成功使用Lambda与Knex。
我正在使用的Lambda运行时是Node.js 8.10
包含以下软件包:
knex: 0.17.0
pg: 7.11.0
下面使用异步的代码也可以正常工作
const Knex = require('knex');
const pg = Knex({ ... });
module.exports. submitForm = async (event) => {
const {
fields,
} = event['body-json'] || {};
return pg('surveys')
.insert(fields)
.then(() => {
return {
status: 200
};
})
.catch(err => {
return {
status: 500
};
});
};
希望它将对将来可能遇到相同问题的人们有所帮助。
我想引起人们注意的是serverless-mysql程序包,该程序包包装了标准的mysql
驱动程序,但处理了该线程中描述的围绕连接池管理的许多lambda特定的痛点。
但是我不认为Knex目前不能使用serverless-mysql
(根据此问题),因为无法交换其他驱动程序。 由于serverless-mysql
使用promise而不是回调,因此也可能存在不兼容性。
最好的方法可能是在Knex中添加新的客户端实现。 我很乐意尝试一下,但是想请一个更熟悉Knex的人告诉我他们是否认为这是合理/可行的?
另外,在思考的同时,我可以使用Knex _build_但不能执行MySQL查询。 因此,只需调用toSQL()
并将输出传递给serverless-mysql
即可执行。
我想知道的是,是否可以在没有任何数据库连接的情况下配置Knex? 打开从未使用过的连接是没有意义的。
以下工作有效吗?
connection = {..., pool: { min: 0, max: 0 ) },
@disbelief您可以在不连接的情况下初始化knex。 在本节末尾的文档https://knexjs.org/#Installation -client中有一个示例
const knex = require('knex')({client: 'mysql'});
const generatedQuery = knex('table').where('id',1).toSQL().toNative();
@elhigu啊,很酷,谢谢。 在过渡期间会给予帮助。
更新:以上方法似乎无效。 如果Knex无法建立db连接,即使没有任何调用执行查询,它也会引发错误。
@disbelief是否找到了解决方案?
@fdecampredon不。 目前,我只是使用squel构建查询,对它们调用toString()
,然后将其传递给serverless-mysql
客户端。
@disbelief令我惊讶的是,没有数据库连接,构建查询失败。
您介意发布用于构建knex客户端的代码和配置吗? 还有knex版本。
以下对我来说很好用
节点v8.11.1
mysql
:2.13.0
knex
:0.18.3
const k = require('knex')
const client = k({ client: 'mysql' })
console.log('Knex version:', require('knex/package.json').version)
// => 0.18.3
console.log('sql:', client('table').where('id',1).toSQL().toNative())
// => { sql: 'select * from `table` where `id` = ?', bindings: [ 1 ] }
根据Lambda的资源回收工作方式,应始终将Knex
实例保留在任何函数或类的外部。 另外,重要的是池中的连接数最多为1。
就像是:
const Knex = require('knex');
let instance = null;
module.exports = class DatabaseManager {
constructor({ host, user, password, database, port = 3306, client = 'mysql', pool = { min: 1, max: 1 }}) {
this._client = client;
this._poolOptions = pool;
this._connectionOptions = {
host: DB_HOST || host,
port: DB_PORT || port,
user: DB_USER || user,
password: DB_PASSWORD || password,
database: DB_NAME || database,
};
}
init() {
if (instance !== null) {
return;
}
instance = Knex({
client: this._client,
pool: this._poolOptions,
connection: this._connectionOptions,
debug: process.env.DEBUG_DB == true,
asyncStackTraces: process.env.DEBUG_DB == true,
});
}
get instance() {
return instance;
}
}
通过这种方式,您可以最大程度地利用Lambda的回收利用,即每个激活(冻结)的容器将仅保持相同的连接。
附带说明一下,请记住,如果您不限制并发容器的数量,则Lambda默认情况下会向外扩展。
我已经让Knex在Lambda中运行了将近一年,没有任何问题。 我在Lambda函数之外声明了我的Knex实例,并使用了其他帖子中提到的context.callbackWaitsForEmptyEventLoop = false
。
也就是说,在过去的一天中,Lambda方面似乎有所改变,因为我现在发现Postgres中的连接数量激增。 连接似乎没有关闭。
使用上述方法的其他人在过去的一天左右有看到任何机会吗?
@jamesdixon刚刚阅读了这篇文章,同时重构了我们在lambda上的一些knex实现。 有任何更新吗? context.callbackWaitsForEmptyEventLoop = false
停止工作?
最有用的评论
抱歉,我的句子很中间,我的孩子就像在地板上扔了3升水:1st_place_medal:我稍后会更新上面的评论...