我正在使用mikeal/request进行API调用。我最频繁使用的API之一(Shopify API)。最近发布了一个新的call limit,我发现错误如:
Exceeded 6.0 calls per second for api client. Slow your requests or contact support for higher limits.
我已经升级了,但无论我获得多少带宽,我都必须考虑到这一点。对Shopify API的大多数请求都在async.map()函数内,这些函数循环异步请求并收集正文。
我正在寻找任何帮助,也许是一个已经存在的库,它会绕过请求模块并实际阻塞,休眠,限制,分配,管理同时触发的许多同步请求,并将它们限制为一次说出6
个请求。如果不存在这个项目,我没有问题。我只是不知道如何处理这种情况,我希望有某种标准。
我用mikeal/request打了一张票。
答案 0 :(得分:33)
对于替代解决方案,我使用node-rate-limiter来包装请求函数,如下所示:
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);
});
};
答案 1 :(得分:22)
node-rate-limiter
包simple-rate-limiter似乎是解决此问题的一个非常好的解决方案。
此外,它比async.queue
和var limit = require("simple-rate-limiter");
var request = limit(require("request")).to(10).per(1000);
更容易使用。
这是一个片段,显示如何将所有请求限制为每秒十次。
aTags[i].parent("li")
答案 2 :(得分:14)
我遇到了各种API的相同问题。 AWS也因节流而闻名。
可以使用几种方法。你提到了async.map()函数。你试过async.queue()吗?队列方法应允许您设置固定限制(如6),超过该数量的任何内容都将放入队列中。
另一个有用的工具是oibackoff。如果您从服务器收到错误并重试,该库将允许您退出请求。
包装两个库以确保你的基础都被覆盖是很有用的:async.queue以确保你没有超过限制,并且oibackoff确保你获得另一个镜头来获取你的请求服务器告诉你有错误。
答案 3 :(得分:8)
在异步模块中,此请求的功能已关闭为“不会修复”
有一个使用leakybucket或令牌桶模型的解决方案,它实现了“限制器”npm模块作为RateLimiter。
RateLimiter
,请参见此示例:https://github.com/caolan/async/issues/1314#issuecomment-263715550
另一种方法是使用 PromiseThrottle
,我用过这个,工作示例如下:
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);
});
输出:
myPromiseFunction: 1, 0.031
myPromiseFunction: 2, 0.201
myPromiseFunction: 3, 0.401
myPromiseFunction: 4, 0.602
myPromiseFunction: 5, 0.803
myPromiseFunction: 6, 1.003
myPromiseFunction: 7, 1.204
myPromiseFunction: 8, 1.404
myPromiseFunction: 9, 1.605
myPromiseFunction: 10, 1.806
myPromiseFunction: 11, 2.007
myPromiseFunction: 12, 2.208
myPromiseFunction: 13, 2.409
myPromiseFunction: 14, 2.61
myPromiseFunction: 15, 2.811
All results: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
我们可以清楚地看到输出的速率,即每秒5次调用。
答案 4 :(得分:2)
这是我的解决方案使用库request-promise
或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));
}
答案 5 :(得分:1)
其他解决方案不符合我的口味。经过进一步研究,我发现promise-ratelimit为您提供了一个API,您可以简单地await
:
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
}
上面的示例将确保您最多每2000 ms 对api.example.com
进行一次查询。换句话说,第一个请求不会等待2000毫秒。
答案 6 :(得分:0)
我使用现代香草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;
}
用法:
const throttledRun = throttleAsync(run, 1000);
答案 7 :(得分:0)
我使用async-sema模块处理节流HTTP请求。这意味着它允许您发送具有速率限制的HTTP请求。
这里是一个例子:
一个简单的Node.js服务器,向API添加express-rate-limit
中间件,以便该API具有速率限制功能。假设这是您案例的Shopify API。
server.ts
:
import express from 'express';
import rateLimit from 'express-rate-limit';
import http from 'http';
const port = 3000;
const limiter = new rateLimit({
windowMs: 1000,
max: 3,
message: 'Max RPS = 3',
});
async function createServer(): Promise<http.Server> {
const app = express();
app.get('/place', limiter, (req, res) => {
res.end('Query place success.');
});
return app.listen(port, () => {
console.log(`Server is listening on http://localhost:${port}`);
});
}
if (require.main === module) {
createServer();
}
export { createServer };
在客户端,我们要发送HTTP请求,并发= 3,并且它们之间的每秒上限。我将客户端代码放在一个测试用例中。所以不要觉得奇怪。
server.test.ts
:
import { RateLimit } from 'async-sema';
import rp from 'request-promise';
import { expect } from 'chai';
import { createServer } from './server';
import http from 'http';
describe('20253425', () => {
let server: http.Server;
beforeEach(async () => {
server = await createServer();
});
afterEach((done) => {
server.close(done);
});
it('should throttle http request per second', async () => {
const url = 'http://localhost:3000/place';
const n = 10;
const lim = RateLimit(3, { timeUnit: 1000 });
const resArr: string[] = [];
for (let i = 0; i < n; i++) {
await lim();
const res = await rp(url);
resArr.push(res);
console.log(`[${new Date().toLocaleTimeString()}] request ${i + 1}, response: ${res}`);
}
expect(resArr).to.have.lengthOf(n);
resArr.forEach((res) => {
expect(res).to.be.eq('Query place success.');
});
});
});
测试结果,请注意请求时间
20253425
Server is listening on http://localhost:3000
[8:08:17 PM] request 1, response: Query place success.
[8:08:17 PM] request 2, response: Query place success.
[8:08:17 PM] request 3, response: Query place success.
[8:08:18 PM] request 4, response: Query place success.
[8:08:18 PM] request 5, response: Query place success.
[8:08:18 PM] request 6, response: Query place success.
[8:08:19 PM] request 7, response: Query place success.
[8:08:19 PM] request 8, response: Query place success.
[8:08:19 PM] request 9, response: Query place success.
[8:08:20 PM] request 10, response: Query place success.
✓ should throttle http request per second (3017ms)
1 passing (3s)