在AWS Lambda上使用Node、Postgres时出现“连接意外终止”错误

aiazj4mn  于 11个月前  发布在  Node.js
关注(0)|答案(3)|浏览(118)

我在AWS Lambda上运行了许多Node函数。这些函数一直在使用Node 8运行时,但AWS发出了一个生命周期终止通知,说函数应该升级到最新的LTS。于是,我将我的一个函数升级到使用Node 12。在生产环境中运行了一段时间后,我开始在查询数据库时看到大量的connection terminated unexpectedly错误。
以下是我看到的错误:

  1. connection terminated意外错误
    1.和Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed-这似乎发生在第一次或第二次调用后,看到连接意外终止错误。
    我正在使用Knex.js查询数据库。我运行的是旧版本的knexnode-postgres,最近升级了,看看它是否能解决这个问题,但没有运气。下面是我目前运行的knexnode-postgres的版本:
    "knex": "^0.20.8" "pg": "^7.17.1"
    我对这个特定函数所做的唯一更改是升级到Node 12。我也尝试了Node 10,但同样的问题仍然存在。不幸的是,AWS不允许我降级到Node 8以验证它确实是一个问题。我在Node 8上运行的其他函数都没有遇到这个问题。
    我已经研究了knexnode-postgrestarn.js(Knex连接池库),看看是否有任何相关的问题或解决方案出现,但到目前为止,我还没有任何运气。
    最新消息:
    一个处理程序的例子。请注意,这发生在许多不同的Lambda上,所有Lambda都运行Node 12。
require('../../helpers/knex')

const { Rollbar } = require('@scoutforpets/utils')
const { Email } = require('@scoutforpets/notifications')
const { transaction: tx } = require('objection')
const Invoice = require('../../models/invoice')

// configure rollbar for error logging
const rollbar = Rollbar.configureRollbar(process.env.ROLLBAR_TOKEN)

/**
 *
 * @param {*} event
 */
async function handler (event) {
  const { invoice } = event
  const { id: invoiceId } = invoice

  try {
    return tx(Invoice, async Invoice => {
      // send the receipt
      await Email.Customer.paymentReceipt(invoiceId, true)

      // convert JSON to model
      const i = Invoice.fromJson(invoice)

      // mark the invoice as having been sent
      await i.markAsSent()
    })
  } catch (err) {
    return err
  }
}

module.exports.handler = rollbar.lambdaHandler(handler)

字符串

6ioyuze2

6ioyuze21#

从node.js 10开始,aws lambda使handler成为x1c,所以你必须调整你的代码。
运行时将三个参数传递给处理程序方法。第一个参数是事件对象,它包含来自调用程序的信息。调用程序在调用JavaScript时将此信息作为JSON格式的字符串传递,运行时将其转换为对象。当AWS服务调用您的函数时,事件结构因服务而异。
第二个参数是上下文对象,它包含有关调用、函数和执行环境的信息。在前面的示例中,函数从上下文对象获取日志流的名称,并将其返回给调用程序。
第三个参数callback是一个可以在非JavaScript函数中调用以发送响应的函数。callback函数有两个参数:Error和response。当您调用它时,Lambda会等待事件循环为空,然后将response或error返回给invoker。response对象必须与JSON. stringify兼容。
对于CRFIC函数,您将响应、错误或promise返回到运行库,而不是使用回调。

exports.handler =  async function(event, context, callback) {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2))
  return context.logStreamName
}

字符串
谢谢!

jgwigjjp

jgwigjjp2#

我认为你需要设置正确的连接池配置。
查看文档:https://github.com/marcogrcr/sequelize/blob/patch-1/docs/manual/other-topics/aws-lambda.md

const { Sequelize } = require("sequelize");

let sequelize = null;

async function loadSequelize() {
  const sequelize = new Sequelize(/* (...) */, {
    // (...)
    pool: {
      /*
       * Lambda functions process one request at a time but your code may issue multiple queries
       * concurrently. Be wary that `sequelize` has methods that issue 2 queries concurrently
       * (e.g. `Model.findAndCountAll()`). Using a value higher than 1 allows concurrent queries to
       * be executed in parallel rather than serialized. Careful with executing too many queries in
       * parallel per Lambda function execution since that can bring down your database with an
       * excessive number of connections.
       *
       * Ideally you want to choose a `max` number where this holds true:
       * max * EXPECTED_MAX_CONCURRENT_LAMBDA_INVOCATIONS < MAX_ALLOWED_DATABASE_CONNECTIONS * 0.8
       */
      max: 2,
      /*
       * Set this value to 0 so connection pool eviction logic eventually cleans up all connections
       * in the event of a Lambda function timeout.
       */
      min: 0,
      /*
       * Set this value to 0 so connections are eligible for cleanup immediately after they're
       * returned to the pool.
       */
      idle: 0,
      // Choose a small enough value that fails fast if a connection takes too long to be established.
      acquire: 3000,
      /*
       * Ensures the connection pool attempts to be cleaned up automatically on the next Lambda
       * function invocation, if the previous invocation timed out.
       */
      evict: CURRENT_LAMBDA_FUNCTION_TIMEOUT
    }
  });

  // or `sequelize.sync()`
  await sequelize.authenticate();

  return sequelize;
}

module.exports.handler = async function (event, callback) {
  // re-use the sequelize instance across invocations to improve performance
  if (!sequelize) {
    sequelize = await loadSequelize();
  } else {
    // restart connection pool to ensure connections are not re-used across invocations
    sequelize.connectionManager.initPools();

    // restore `getConnection()` if it has been overwritten by `close()`
    if (sequelize.connectionManager.hasOwnProperty("getConnection")) {
      delete sequelize.connectionManager.getConnection;
    }
  }

  try {
    return await doSomethingWithSequelize(sequelize);
  } finally {
    // close any opened connections during the invocation
    // this will wait for any in-progress queries to finish before closing the connections
    await sequelize.connectionManager.close();
  }
};

字符串
这实际上是续集,而不是膝盖,但我相信在引擎盖下,他们的工作方式是一样的。

0wi1tuuw

0wi1tuuw3#

我也有这个问题,在我的情况下,这是因为我试图在生产中连接数据库。
所以,我把ssl添加到Pool中,像这样:

const pool = new Pool({
    connectionString: connectionString,
    ssl: {rejectUnauthorized: false},
});

字符串
希望对你也有帮助…

相关问题