2013-11-27 31 views
35

我使用mikeal/request進行API調用。我最常使用的API之一(Shopify API)。最近推出了一個新的call limit,我看到這樣的錯誤:節流並排隊API申請由於每秒帽

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

沒有開玩笑。我終於厭倦了ElasticTranscoder UI,並通過JS SDK構建了使用API​​的代碼,並立即達到這些限制。 – rainabba

回答

11

我遇到了與各種API相同的問題。 AWS也因節流而聞名。

可以使用幾種方法。你提到了async.map()函數。你有沒有試過async.queue()?隊列方法應該允許你設置一個可靠的限制(比如6),超過這個數量的任何數據將被放入隊列中。

另一個有用的工具是oibackoff。如果從服務器收到錯誤並重試,該庫將允許您退出請求。

包裝這兩個庫以確保您的基礎都被覆蓋是非常有用的:async.queue確保您不會超出限制,oibackoff可確保您獲得另一個鏡頭,以便在if服務器告訴你有錯誤。

+1

我會深入研究這兩點建議。我遇到的唯一問題是我的'async.maps'分散在彼此之中。所以我不能用'async.queue'替換它們,因爲我仍然不能保證對API的請求一次是6。他們將是6 *每個'async.queue'。但我認爲球正在滾動? – ThomasReggi

+0

https://caolan.github.io/async/docs.html#queue不會節流(每秒/分鐘)。這只是一些異步操作。 –

28

對於一個替代解決方案,我用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); 
    }); 
}; 
+0

我要去看看這個!謝謝一堆! – ThomasReggi

+12

這裏是節點限速器的作者。這個庫可能會更適合所述的問題,因爲async.queue()僅限於併發性,沒有時間概念。 API速率限制通常是基於時間的(即每秒最多6次調用),可以表示爲'var limiter = new RateLimiter(6,'second');'這是對像oibackoff這樣的解決方案的補充,費率限制已經達到。 – jhurliman

+0

我可以爲所有請求做整體或需要單獨做?我的意思是我可以把它放在我的中間件裏面嗎?如果是,它將如何應用於所有端點或每個端點? –

2

這裏是我的解決方案中使用的庫request-promiseaxios,敷在這一承諾的呼籲。

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)); 
    } 
16

npmsimple-rate-limiter似乎是一個很好的解決了這個問題。

此外,它比node-rate-limiterasync.queue更容易使用。

下面是一段代碼,展示瞭如何將每個請求限制爲每秒十個。

var limit = require("simple-rate-limiter"); 
var request = limit(require("request")).to(10).per(1000); 
3

在異步模塊,該請求的功能被關閉如在2016年獲得「不會修復」

有使用leakybucket或令牌桶模型的解決方案,它的實現「限幅」 NPM模塊作爲RateLimiter

參見例如這裏:https://github.com/caolan/async/issues/1314#issuecomment-263715550

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次調用。