2013-11-27 16 views
35

Tôi đang sử dụng mikeal/request để thực hiện cuộc gọi API. Một trong những API tôi sử dụng thường xuyên nhất (API Shopify). Gần đây đã đưa ra một mới call limit, tôi nhìn thấy các lỗi như:Điều chỉnh và xếp hàng các yêu cầu API do mỗi giới hạn thứ hai

Exceeded 6.0 calls per second for api client. Slow your requests or contact support for higher limits.

Tôi đã nhận được một bản nâng cấp, nhưng bất kể có bao nhiêu băng thông tôi nhận được tôi có vào tài khoản cho việc này. Phần lớn các yêu cầu đối với API Shopify nằm trong các hàm async.map(), lặp lại các yêu cầu không đồng bộ và thu thập các phần tử.

Tôi đang tìm kiếm bất kỳ trợ giúp nào, có thể là thư viện đã tồn tại, sẽ bao quanh mô-đun yêu cầu và chặn, ngủ, điều tiết, phân bổ, quản lý, nhiều yêu cầu đồng thời được kích hoạt không đồng bộ và giới hạn chúng để nói 6 yêu cầu cùng một lúc. Tôi không có vấn đề với làm việc trên một dự án như vậy nếu nó không tồn tại. Tôi chỉ không biết cách xử lý tình huống như thế này, và tôi hy vọng một loại tiêu chuẩn nào đó.

Tôi đã mua vé với mikeal/request.

+0

Không đùa. Cuối cùng tôi đã chán ngấy với giao diện người dùng ElasticTranscoder và xây dựng mã để sử dụng API thông qua JS SDK và ngay lập tức đạt đến các giới hạn này. – rainabba

Trả lời

11

Tôi đã gặp sự cố tương tự với các API khác nhau. AWS nổi tiếng với điều chỉnh là tốt.

Có thể sử dụng một vài phương pháp tiếp cận. Bạn đã đề cập đến hàm async.map(). Bạn đã thử async.queue() chưa? Phương thức xếp hàng sẽ cho phép bạn đặt giới hạn vững chắc (như 6) và bất kỳ thứ gì vượt quá số tiền đó sẽ được đặt trong hàng đợi.

Một công cụ hữu ích khác là oibackoff. Thư viện đó sẽ cho phép bạn sao lưu yêu cầu của mình nếu bạn gặp lỗi từ máy chủ và thử lại.

Có thể hữu ích khi bao bọc hai thư viện để đảm bảo rằng cả hai cơ sở của bạn đều được bảo vệ: async.queue để đảm bảo bạn không vượt quá giới hạn và oibackoff để đảm bảo bạn nhận được một shot khác khi nhận được yêu cầu của bạn nếu máy chủ cho bạn biết đã xảy ra lỗi.

+1

Tôi sẽ đào sâu vào hai đề xuất đó. Vấn đề duy nhất tôi có là "async.maps' của tôi được trải ra và lồng vào nhau. Vì vậy, tôi không thể thay thế chúng bằng 'async.queue' vì tôi sẽ không đảm bảo rằng các yêu cầu đối với API sẽ là 6 tại một thời điểm. Chúng sẽ là 6 * mỗi 'async.queue'. Nhưng tôi nghĩ quả bóng đang lăn? – ThomasReggi

+0

https://caolan.github.io/async/docs.html#queue sẽ không tăng tốc (mỗi giây/phút). Nó chỉ là số hoạt động không đồng bộ. –

28

Đối với một giải pháp thay thế, tôi đã sử dụng node-rate-limiter để bọc các chức năng yêu cầu như thế này:

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

Tôi sẽ xem xét điều này! Cảm ơn nhiều! – ThomasReggi

+12

Tác giả của nút-tỷ lệ giới hạn ở đây. Thư viện này có lẽ sẽ phù hợp hơn với vấn đề đã nêu, vì async.queue() chỉ đặt giới hạn về đồng thời và không có khái niệm về thời gian. Giới hạn tốc độ API thường dựa trên thời gian (nghĩa là tối đa 6 cuộc gọi mỗi giây) có thể được biểu thị dưới dạng 'var limiter = new RateLimiter (6, 'second');' Nó bổ sung cho một giải pháp như oibackoff sẽ thay đổi hành vi sau giới hạn tốc độ đã bị ảnh hưởng. – jhurliman

+0

Tôi có thể làm điều đó cho tất cả các yêu cầu toàn bộ hoặc cần phải thực hiện riêng lẻ không? Tôi có nghĩa là tôi có thể đặt nó bên trong middleware của tôi? Nếu có, làm thế nào nó sẽ được áp dụng, cho tất cả các thiết bị đầu cuối hoặc cho mỗi thiết bị đầu cuối? –

2

Đây là giải pháp của tôi sử dụng một thư viện request-promise hoặc axios và quấn cuộc gọi trong lời hứa này.

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

Gói npmsimple-rate-limiter có vẻ là giải pháp rất tốt cho vấn đề này.

Hơn nữa, việc sử dụng dễ dàng hơn node-rate-limiterasync.queue.

Dưới đây là một đoạn mã cho biết cách giới hạn tất cả các yêu cầu đến mười mỗi giây.

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

Trong mô-đun async, tính năng được yêu cầu này đã bị đóng như "sửa chữa sẽ không"

Có một giải pháp sử dụng leakybucket hoặc mô hình xô token, nó được thực hiện "giới hạn" mô-đun NPM như RateLimiter

Xem ví dụ ở đây:. 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); 
     }); 

Output:

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 

Chúng tôi có thể thấy rõ tỷ lệ từ đầu ra, tức là 5 cuộc gọi cho mỗi giây.

Các vấn đề liên quan