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
这可能会发生,因为 innercallback
在 else
的情况下被同步调用。应该是:
} 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);
我有一个 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
这可能会发生,因为 innercallback
在 else
的情况下被同步调用。应该是:
} 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);