【问题标题】:How can the AWS Lambda concurrent execution limit be reached?如何达到 AWS Lambda 并发执行限制?
【发布时间】:2019-02-13 11:54:51
【问题描述】:

更新

下面的原始测试代码基本上是正确的,但在 NodeJS 中,各种 AWS 服务的设置应与 @Michael-sqlbot 提供的 SDK link 有所不同

// manager
const AWS = require("aws-sdk")
const https = require('https');
const agent = new https.Agent({
    maxSockets: 498 // workers hit this level; expect plus 1 for the manager instance
});
const lambda = new AWS.Lambda({
    apiVersion: '2015-03-31',
    region: 'us-east-2', // Initial concurrency burst limit = 500
    httpOptions: {   // <--- replace the default of 50 (https) by
        agent: agent // <--- plugging the modified Agent into the service
    }
})
// NOW begin the manager handler code

在规划一项新服务时,我正在进行一些初步的压力测试。在阅读了每个帐户的 the 1,000 concurrent execution limit 和 initial burst rate(在 us-east-2 中为 500)之后,我期望立即实现至少 500 个突发并发执行。 CloudWatch 的 Lambda 指标的屏幕截图以其他方式显示。 无论我尝试何种参数组合,我都无法超过 51 个并发执行。这是测试代码:

// worker
exports.handler = async (event) => {
    // declare sleep promise
    const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    // return after one second
    let nStart = new Date().getTime()
    await sleep(1000)
    return new Date().getTime() - nStart; // report the exact ms the sleep actually took
};

// manager
exports.handler = async(event) => {
    const invokeWorker = async() => {
        try {
            let lambda = new AWS.Lambda() // NO! DO NOT DO THIS, SEE UPDATE ABOVE
            var params = {
                FunctionName: "worker-function",
                InvocationType: "RequestResponse",
                LogType: "None"
            };
            return await lambda.invoke(params).promise()

        }
        catch (error) {
            console.log(error)
        }
    };

    try {
        let nStart = new Date().getTime()
        let aPromises = []

        // invoke workers
        for (var i = 1; i <= 3000; i++) {
            aPromises.push(invokeWorker())
        }

        // record time to complete spawning
        let nSpawnMs = new Date().getTime() - nStart

        // wait for the workers to ALL return
        let aResponses = await Promise.all(aPromises)

        // sum all the actual sleep times
        const reducer = (accumulator, response) => { return accumulator + parseInt(response.Payload) };
        let nTotalWorkMs = aResponses.reduce(reducer, 0)

        // show me
        let nTotalET = new Date().getTime() - nStart
        return {
            jobsCount: aResponses.length,
            spawnCompletionMs: nSpawnMs,
            spawnCompletionPct: `${Math.floor(nSpawnMs / nTotalET * 10000) / 100}%`,
            totalElapsedMs: nTotalET,
            totalWorkMs: nTotalWorkMs,
            parallelRatio: Math.floor(nTotalET / nTotalWorkMs * 1000) / 1000
        }
    }

    catch (error) {
        console.log(error)
    }
};

Response:
{
  "jobsCount": 3000,
  "spawnCompletionMs": 1879,
  "spawnCompletionPct": "2.91%",
  "totalElapsedMs": 64546,
  "totalWorkMs": 3004205,
  "parallelRatio": 0.021
}

Request ID:
"43f31584-238e-4af9-9c5d-95ccab22ae84"

我是否达到了我没有提到的其他限制?我的测试代码有缺陷吗?我试图在这里达到 3,000 名工作人员的限制,但没有遇到任何限制,我猜这是由于异步调用重试行为。

编辑:任一 Lambda 均不涉及 VPC;选择输入中的设置是“No VPC”。

编辑:在修复前后显示 Cloudwatch

【问题讨论】:

  • 您的 AWS Lambda 函数的配置是什么?是否在 VPC 中?
  • “我猜这是由于异步调用重试行为造成的。” 您正在使用InvocationType: "RequestResponse" - 这意味着同步,而不是异步,即使您的处理程序是async 函数。服务不会重试。但是,如果您也将调用程序作为 lambda 函数运行,那么除非该调用程序函数的容器有很多可用的 CPU 周期(您可以通过增加内存来获得),否则它可能没有资源来生成,签名,并提交足够的同时请求以正确执行测试。也许在 EC2 中运行它。
  • @Michael-sqlbot ROFL!从那个LOL中恢复需要一点时间。所以,实际上,这很方便,不是吗!知道 us-east-2 在初始爆发时只会给您 500,因此可以将其设置为 495 并且永远不要担心会达到 AWS 的油门! Node 的缓存超出了 maxSockets,这(可能)会导致大负载的内存问题,所以有一个小问题,但这可能是次要的。正如我的测试所示,1024MB 以上的性能提升可以忽略不计。好的,现在重新编写这个测试.....
  • @Michael-sqlbot - 你的 Homer Simpson 链接让我现在在做Tim Allen power tool noises!查看更新的屏幕截图。修改后的代码将parallelRatio从0.022粉碎到0.008!这很有趣!!如果你在回答表格中写了一个,我可以让你检查。 我经常在俄勒冈州立大学看望我的孩子,但并没有完全了解“谁戴”县。下次我欠你一些啤酒!谢谢。

标签: node.js amazon-web-services aws-lambda


【解决方案1】:

有许多潜在的嫌疑人,特别是因为您从 Lambda 调用 Lambda,但您始终关注 50 的并发性——一个看似任意的限制(和一个可疑的整数)——提醒我JavaScript SDK 中潜伏着一个反足枪:

在 Node.js 中,您可以设置每个源的最大连接数。如果设置了 maxSockets,则低级 HTTP 客户端将请求排队并在它们可用时将它们分配给套接字。

当然,这里的“来源”是指方案 + 主机名的任何唯一组合,在这种情况下,它是 SDK 连接到的用于调用 @987654323 的 us-east-2 中 Lambda 的服务 endpoint @方法,https://lambda.us-east-2.amazonaws.com。

这使您可以设置一次对给定源的并发请求数的上限。降低此值可以减少收到的限制或超时错误的数量。但是,它也会增加内存使用量,因为请求会排队等待套接字可用。

...

使用默认值https 时,SDK 会从globalAgent 中获取maxSockets 值。如果 maxSockets 值未定义或为 Infinity,则 SDK 假定 maxSockets 值为 50。

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-configuring-maxsockets.html

【讨论】:

  • 是的,整数“50”对我来说就像一个限制。感谢您的提示。
【解决方案2】:

Lambda 并发并不是决定函数可扩展性的唯一因素。如果您的 Lambda 函数在 VPC 中运行,则需要一个 ENI(弹性网络接口),该接口允许以太网流量进出容器(Lambda 函数)。

您的限制可能是由于请求的 ENI 太多(一次 50 个)。您可以通过查看 Manager lambda 函数的日志并在它尝试调用其中一个子容器时查找错误消息来检查这一点。如果错误类似于以下内容,您就会知道 ENI 是您的问题。

Lambda was not able to create an ENI in the VPC of the Lambda function because the limit for Network Interfaces has been reached.

【讨论】:

猜你喜欢
  • 2016-06-07
  • 2018-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多