Node.js 由于每秒上限而限制和排队API请求
我习惯于进行API调用。我最常用的API之一(Shopify API)。最近发布了一个新的,我看到的错误如下:Node.js 由于每秒上限而限制和排队API请求,node.js,asynchronous,request,throttling,Node.js,Asynchronous,Request,Throttling,我习惯于进行API调用。我最常用的API之一(Shopify API)。最近发布了一个新的,我看到的错误如下: Exceeded 6.0 calls per second for api client. Slow your requests or contact support for higher limits. 我已经得到了升级,但不管我得到多少带宽,我必须对此负责。对Shopify API的大部分请求都在函数内,函数循环异步请求并收集主体 我正在寻找任何帮助,可能是一个已经存在的库,它将
Exceeded 6.0 calls per second for api client. Slow your requests or contact support for higher limits.
我已经得到了升级,但不管我得到多少带宽,我必须对此负责。对Shopify API的大部分请求都在函数内,函数循环异步请求并收集主体
我正在寻找任何帮助,可能是一个已经存在的库,它将围绕请求模块,实际阻止、睡眠、调节、分配、管理异步启动的多个并发请求,并将它们限制为一次发出6
请求。如果这样一个项目不存在,我对它的工作没有问题。我只是不知道如何处理这种情况,我希望有某种标准
我用做了一张罚单。我在各种API中遇到了相同的问题。AWS也以节流而闻名 可以使用几种方法。您提到了async.map()函数。你试过了吗?queue方法应该允许您设置一个固定限制(如6),超过该限制的任何内容都将放入队列中 另一个有用的工具是。如果从服务器返回错误并重试,该库将允许您回退请求
包装这两个库以确保两个库都被覆盖是很有用的:async.queue以确保您不超过限制,oibackoff以确保在服务器告诉您有错误时,您可以再次获得请求。对于另一种解决方案,我使用了如下包装请求函数:
var request = require('request');
var RateLimiter = require('limiter').RateLimiter;
var limiter = new RateLimiter(1, 100); // at most 1 request every 100 ms
var throttledRequest = function() {
var requestArgs = arguments;
limiter.removeTokens(1, function() {
request.apply(this, requestArgs);
});
};
下面是我的解决方案,使用库
请求承诺
或axios
并将调用封装在这个承诺中
var Promise = require("bluebird")
// http://stackoverflow.com/questions/28459812/way-to-provide-this-to-the-global-scope#28459875
// http://stackoverflow.com/questions/27561158/timed-promise-queue-throttle
module.exports = promiseDebounce
function promiseDebounce(fn, delay, count) {
var working = 0, queue = [];
function work() {
if ((queue.length === 0) || (working === count)) return;
working++;
Promise.delay(delay).tap(function () { working--; }).then(work);
var next = queue.shift();
next[2](fn.apply(next[0], next[1]));
}
return function debounced() {
var args = arguments;
return new Promise(function(resolve){
queue.push([this, args, resolve]);
if (working < count) work();
}.bind(this));
}
var Promise=require(“蓝鸟”)
// http://stackoverflow.com/questions/28459812/way-to-provide-this-to-the-global-scope#28459875
// http://stackoverflow.com/questions/27561158/timed-promise-queue-throttle
module.exports=promisedbounce
功能承诺(fn、延迟、计数){
var working=0,queue=[];
职能工作(){
if((queue.length==0)| |(working==count))返回;
工作++;
delay(delay).tap(函数(){working--;}).then(work);
var next=queue.shift();
next[2](fn.apply(next[0],next[1]);
}
返回函数去Bounced(){
var args=参数;
返回新承诺(函数(解析){
push([this,args,resolve]);
如果(工作<计数)工作();
}.约束(这个);
}
npm
包似乎是解决这个问题的一个很好的方法
此外,它比节点速率限制器
和异步队列
更易于使用
下面是一个片段,演示如何将所有请求限制为每秒10个
var limit = require("simple-rate-limiter");
var request = limit(require("request")).to(10).per(1000);
在异步模块中,此请求的功能被关闭为“不会修复”
- 2016年给出的理由是“正确管理此类结构是必要的
这是一个难题。”请参见此处右侧:
- 2013年给出的理由是“无法扩展到多个流程”,见:
有一个解决方案使用漏桶或令牌桶模型,它实现了“限制器”npm模块作为速率限制器
费率限制器
,参见此处示例:
另一种方法是使用PromiseTrottle,我使用了这个,工作示例如下:
var PromiseThrottle = require('promise-throttle');
let RATE_PER_SECOND = 5; // 5 = 5 per second, 0.5 = 1 per every 2 seconds
var pto = new PromiseThrottle({
requestsPerSecond: RATE_PER_SECOND, // up to 1 request per second
promiseImplementation: Promise // the Promise library you are using
});
let timeStart = Date.now();
var myPromiseFunction = function (arg) {
return new Promise(function (resolve, reject) {
console.log("myPromiseFunction: " + arg + ", " + (Date.now() - timeStart) / 1000);
let response = arg;
return resolve(response);
});
};
let NUMBER_OF_REQUESTS = 15;
let promiseArray = [];
for (let i = 1; i <= NUMBER_OF_REQUESTS; i++) {
promiseArray.push(
pto
.add(myPromiseFunction.bind(this, i)) // passing am argument using bind()
);
}
Promise
.all(promiseArray)
.then(function (allResponsesArray) { // [1 .. 100]
console.log("All results: " + allResponsesArray);
});
我们可以从输出中清楚地看到速率,即每秒5次调用。其他解决方案不符合我的口味。进一步研究后,我发现它提供了一个api,您可以简单地等待它:
var rate = 2000 // in milliseconds
var throttle = require('promise-ratelimit')(rate)
async function queryExampleApi () {
await throttle()
var response = await get('https://api.example.com/stuff')
return response.body.things
}
上面的示例将确保您最多每2000ms查询一次api.example.com
。换句话说,第一个请求不会等待2000ms。使用现代vanilla JS的我的解决方案:
function throttleAsync(fn, wait) {
let lastRun = 0;
async function throttled(...args) {
const currentWait = lastRun + wait - Date.now();
const shouldRun = currentWait <= 0;
if (shouldRun) {
lastRun = Date.now();
return await fn(...args);
} else {
return await new Promise(function(resolve) {
setTimeout(function() {
resolve(throttled());
}, currentWait);
});
}
}
return throttled;
}
我使用模块处理HTTP请求。这意味着它允许您以速率限制发送HTTP请求
以下是一个例子:
一个简单的Node.js服务器,在API中添加express rate limit
中间件,使API具有速率限制功能。假设这是适用于您案例的Shopify API
server.ts
:
从“express”导入express;
进口税率限制来自“明示税率限制”;
从“http”导入http;
常数端口=3000;
常数限制器=新的速率限制({
windowMs:1000,
最高:3,
消息:“最大RPS=3”,
});
异步函数createServer():Promise{
常量app=express();
应用程序获取('/place',限制器,(请求,恢复)=>{
res.end('查询位置成功');
});
返回应用程序。侦听(端口,()=>{
log(`服务器正在侦听http://localhost:${port}`);
});
}
if(require.main==模块){
createServer();
}
导出{createServer};
在客户端,我们希望发送并发度为3的HTTP请求,并且在它们之间设置每秒上限。我将客户端代码放在一个测试用例中。所以不要觉得奇怪
server.test.ts
:
从'async sema'导入{RateLimit};
从“请求-承诺”导入rp;
从“chai”导入{expect};
从“/server”导入{createServer};
从“http”导入http;
描述('20253425',()=>{
let服务器:http.server;
beforeach(异步()=>{
服务器=等待createServer();
});
每次之后((完成)=>{
服务器关闭(完成);
});
它('每秒应限制http请求',异步()=>{
常量url=http://localhost:3000/place';
常数n=10;
const lim=速率限制(3,{时间单位:1000});
常量resArr:string[]=[];
for(设i=0;i{
expect(res.to.be.eq('queryplacesuccess');
});
});
function throttleAsync(fn, wait) {
let lastRun = 0;
async function throttled(...args) {
const currentWait = lastRun + wait - Date.now();
const shouldRun = currentWait <= 0;
if (shouldRun) {
lastRun = Date.now();
return await fn(...args);
} else {
return await new Promise(function(resolve) {
setTimeout(function() {
resolve(throttled());
}, currentWait);
});
}
}
return throttled;
}
const throttledRun = throttleAsync(run, 1000);