Nodejs - 在限制速率的同时触发多个 API 调用并等待它们全部完成
Nodejs - Fire multiple API calls while limiting the rate and wait until they are all done
我的问题
- 启动 1000+ 在线 API 将 API 调用次数限制为 10 calls/sec.
- 等待所有 API 调用返回结果(或重试),API 可能需要 5 秒才能向其发送数据
- 在我的应用程序的其余部分使用组合数据
我在网站上查看许多不同的问题和答案时尝试过的方法
使用 promise 等待一个 API 请求
const https = require("https");
function myRequest(param) {
const options = {
host: "api.xxx.io",
port: 443,
path: "/custom/path/"+param,
method: "GET"
}
return new Promise(function(resolve, reject) {
https.request(options, function(result) {
let str = "";
result.on('data', function(chunk) {str += chunk;});
result.on('end', function() {resolve(JSON.parse(str));});
result.on('error', function(err) {console.log("Error: ", err);});
}).end();
});
};
使用Promise.all完成所有请求并等待它们完成
const params = [{item: "param0"}, ... , {item: "param1000+"}]; // imagine 1000+ items
const promises = [];
base.map(function(params){
promises.push(myRequest(params.item));
});
result = Promise.all(promises).then(function(data) {
// doing some funky stuff with dat
});
到目前为止还不错,有点
当我将 API 请求的数量限制为最多 10 个时,它会起作用,因为那时速率限制器会启动。当我 console.log(promises), 它返回一个 'request'.
的数组
我尝试在不同的地方添加 setTimeout,例如:
...
base.map(function(params){
promises.push(setTimeout(function() {
myRequest(params.item);
}, 100));
});
...
但这似乎不起作用。当我 console.log(promises) 时,它返回一个 'function'
的数组
我的问题
- 现在我卡住了……有什么想法吗?
- 当 API 出错时如何构建重试
感谢你的阅读,你已经是我心中的英雄了!
当你有一个复杂的 control-flow 时,使用 async/await 有助于阐明流程的逻辑。
让我们从以下简单算法开始,将所有请求限制为每秒 10 个请求:
make 10 requests
wait 1 second
repeat until no more requests
为此,以下简单的实现将起作用:
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) { // use shift instead
batch.push(myRequest(thisParam.item)); // of pop if you want
} // to process in the
// original order.
}
results = results.concat(await Promise.all(batch));
await delayOneSecond();
}
return results;
}
现在我们只需要实现一秒延迟。我们可以简单地为此承诺 setTimeout:
function delayOneSecond() {
return new Promise(ok => setTimeout(ok, 1000));
}
这肯定会给您每秒仅 10 个请求的速率限制器。事实上,它的执行速度比那慢一些,因为每个批处理将在请求时间+一秒内执行。这非常好并且已经满足您的初衷,但我们可以改进它以压缩更多请求以尽可能接近每秒 10 个请求。
我们可以试试下面的算法:
remember the start time
make 10 requests
compare end time with start time
delay one second minus request time
repeat until no more requests
同样,我们可以使用与上面的简单代码几乎完全相同的逻辑,但只需对其进行调整以进行时间计算:
const ONE_SECOND = 1000;
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
let startTime = Date.now();
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) {
batch.push(myRequest(thisParam.item));
}
}
results = results.concat(await Promise.all(batch));
let endTime = Date.now();
let requestTime = endTime - startTime;
let delayTime = ONE_SECOND - requestTime;
if (delayTime > 0) {
await delay(delayTime);
}
}
return results;
}
现在我们可以编写一个接受延迟周期的函数,而不是硬编码一秒延迟函数:
function delay(milliseconds) {
return new Promise(ok => setTimeout(ok, milliseconds));
}
我们这里有一个简单易懂的函数,可以将速率限制为尽可能接近每秒 10 个请求。它相当突发,因为它在每一秒周期的开始发出 10 个并行请求,但它有效。我们当然可以继续实施更复杂的算法来平滑请求模式等,但我将其留给您的创造力并作为 reader.
的家庭作业。
我的问题
- 启动 1000+ 在线 API 将 API 调用次数限制为 10 calls/sec.
- 等待所有 API 调用返回结果(或重试),API 可能需要 5 秒才能向其发送数据
- 在我的应用程序的其余部分使用组合数据
我在网站上查看许多不同的问题和答案时尝试过的方法
使用 promise 等待一个 API 请求
const https = require("https");
function myRequest(param) {
const options = {
host: "api.xxx.io",
port: 443,
path: "/custom/path/"+param,
method: "GET"
}
return new Promise(function(resolve, reject) {
https.request(options, function(result) {
let str = "";
result.on('data', function(chunk) {str += chunk;});
result.on('end', function() {resolve(JSON.parse(str));});
result.on('error', function(err) {console.log("Error: ", err);});
}).end();
});
};
使用Promise.all完成所有请求并等待它们完成
const params = [{item: "param0"}, ... , {item: "param1000+"}]; // imagine 1000+ items
const promises = [];
base.map(function(params){
promises.push(myRequest(params.item));
});
result = Promise.all(promises).then(function(data) {
// doing some funky stuff with dat
});
到目前为止还不错,有点
当我将 API 请求的数量限制为最多 10 个时,它会起作用,因为那时速率限制器会启动。当我 console.log(promises), 它返回一个 'request'.
的数组我尝试在不同的地方添加 setTimeout,例如:
...
base.map(function(params){
promises.push(setTimeout(function() {
myRequest(params.item);
}, 100));
});
...
但这似乎不起作用。当我 console.log(promises) 时,它返回一个 'function'
的数组我的问题
- 现在我卡住了……有什么想法吗?
- 当 API 出错时如何构建重试
感谢你的阅读,你已经是我心中的英雄了!
当你有一个复杂的 control-flow 时,使用 async/await 有助于阐明流程的逻辑。
让我们从以下简单算法开始,将所有请求限制为每秒 10 个请求:
make 10 requests
wait 1 second
repeat until no more requests
为此,以下简单的实现将起作用:
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) { // use shift instead
batch.push(myRequest(thisParam.item)); // of pop if you want
} // to process in the
// original order.
}
results = results.concat(await Promise.all(batch));
await delayOneSecond();
}
return results;
}
现在我们只需要实现一秒延迟。我们可以简单地为此承诺 setTimeout:
function delayOneSecond() {
return new Promise(ok => setTimeout(ok, 1000));
}
这肯定会给您每秒仅 10 个请求的速率限制器。事实上,它的执行速度比那慢一些,因为每个批处理将在请求时间+一秒内执行。这非常好并且已经满足您的初衷,但我们可以改进它以压缩更多请求以尽可能接近每秒 10 个请求。
我们可以试试下面的算法:
remember the start time
make 10 requests
compare end time with start time
delay one second minus request time
repeat until no more requests
同样,我们可以使用与上面的简单代码几乎完全相同的逻辑,但只需对其进行调整以进行时间计算:
const ONE_SECOND = 1000;
async function rateLimitedRequests (params) {
let results = [];
while (params.length > 0) {
let batch = [];
let startTime = Date.now();
for (i=0; i<10; i++) {
let thisParam = params.pop();
if (thisParam) {
batch.push(myRequest(thisParam.item));
}
}
results = results.concat(await Promise.all(batch));
let endTime = Date.now();
let requestTime = endTime - startTime;
let delayTime = ONE_SECOND - requestTime;
if (delayTime > 0) {
await delay(delayTime);
}
}
return results;
}
现在我们可以编写一个接受延迟周期的函数,而不是硬编码一秒延迟函数:
function delay(milliseconds) {
return new Promise(ok => setTimeout(ok, milliseconds));
}
我们这里有一个简单易懂的函数,可以将速率限制为尽可能接近每秒 10 个请求。它相当突发,因为它在每一秒周期的开始发出 10 个并行请求,但它有效。我们当然可以继续实施更复杂的算法来平滑请求模式等,但我将其留给您的创造力并作为 reader.
的家庭作业。