Node.js - 限制我发出的请求数量

Node.js - Limiting the number of requests I make

我有一个 Node 应用程序,其中有一个 Gremlin 客户端:

var Gremlin = require('gremlin');

const client = Gremlin.createClient(
    443,
    config.endpoint,
    {
        "session": false,
        "ssl": true,
        "user": `/dbs/${config.database}/colls/${config.collection}`,
        "password": config.primaryKey
    }
);

然后我使用它调用 CosmoDB 以添加一些记录:

async.forEach(pData, function (data, innercallback) {
    if (data.type == 'Full'){
      client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {}, innercallback);
    } else {
        innercallback(null);
    }
}, outercallback);

但是在我的 Azure 端有每秒 400 个请求的限制,随后我收到错误:

ExceptionType : RequestRateTooLargeException
ExceptionMessage : Message: {"Errors":["Request rate is large"]}

有没有人知道我如何限制每秒发出的请求数,而不必在 Azure 上扩展(因为那样成本更高:))

另外:

我尝试使用

async.forEachLimit(pData, 400, function (data, innercallback) {
    if (data.type == 'Full'){
      client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {}, innercallback);
    } else {
        innercallback(null);
    }
}, outercallback);

但是,如果继续看到 RangeError: Maximum call stack size exceeded 如果它太高,否则如果我减少我只会得到相同的请求率太大异常。

谢谢。

我们先弄清楚一些事情。您没有 400 requests/second collection,而是 400 RU/s collection。 RU 代表请求单位,它们不会转换为请求。

大致:

  • 检索 1KB 文档的检索请求将花费 1 RU。
  • 检索 1KB 文档的修改将花费 5 RU。

假设您的文档有 1KB 大,您每秒只能添加 80 个文档。

现在我们已经解决了这个问题,听起来 async.queue() 可以为您解决问题。

RangeError: Maximum call stack size exceeded

这可能会发生,因为 innercallbackelse 的情况下被同步调用。应该是:

} else {
    process.nextTick(function() {
        innercallback(null)
    });
}

forEachLimit的调用看起来大体上是正确的,但你需要确保当一个请求真的被触发时(if块),innercallback不会早于1调用其次要保证一秒钟内不超过 400 个请求被触发。最简单的就是延迟回调执行恰好1秒:

client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {},
function(err) {
    setTimeout(function() { innercallback(err); }, 1000);
});

更准确的解决方案是计算实际的请求+响应时间,setTimeout只计算剩余的1秒。

作为进一步的改进,看起来您可以在执行异步操作之前过滤 pData 数组以摆脱 if...else,因此最终:

var pDataFull = pData.filter(function(data) => {
    return data.type == 'Full';
});

async.forEachLimit(pDataFull, 400, function (data, innercallback) {
    client.execute("g.addV('test').property('id', \"" + data.$.id + 
        "\")", {},
        function(err) {
            setTimeout(function() { innercallback(err); }, 1000);
        }
    );
}, outercallback);