141 lines
5.2 KiB
Markdown
141 lines
5.2 KiB
Markdown
# limiter #
|
||
|
||
[![Build Status](https://travis-ci.org/jhurliman/node-rate-limiter.png)](https://travis-ci.org/jhurliman/node-rate-limiter)
|
||
[![NPM Downloads](https://img.shields.io/npm/dm/limiter.svg?style=flat)](https://www.npmjs.com/package/limiter)
|
||
|
||
Provides a generic rate limiter for node.js. Useful for API clients, web
|
||
crawling, or other tasks that need to be throttled. Two classes are exposed,
|
||
RateLimiter and TokenBucket. TokenBucket provides a lower level interface to
|
||
rate limiting with a configurable burst rate and drip rate. RateLimiter sits
|
||
on top of the token bucket and adds a restriction on the maximum number of
|
||
tokens that can be removed each interval to comply with common API
|
||
restrictions like "150 requests per hour maximum".
|
||
|
||
## Installation ##
|
||
|
||
Use NPM to install:
|
||
|
||
npm install limiter
|
||
|
||
## Usage ##
|
||
|
||
A simple example allowing 150 requests per hour:
|
||
|
||
```javascript
|
||
var RateLimiter = require('limiter').RateLimiter;
|
||
// Allow 150 requests per hour (the Twitter search limit). Also understands
|
||
// 'second', 'minute', 'day', or a number of milliseconds
|
||
var limiter = new RateLimiter(150, 'hour');
|
||
|
||
// Throttle requests
|
||
limiter.removeTokens(1, function(err, remainingRequests) {
|
||
// err will only be set if we request more than the maximum number of
|
||
// requests we set in the constructor
|
||
|
||
// remainingRequests tells us how many additional requests could be sent
|
||
// right this moment
|
||
|
||
callMyRequestSendingFunction(...);
|
||
});
|
||
```
|
||
|
||
Another example allowing one message to be sent every 250ms:
|
||
|
||
```javascript
|
||
var RateLimiter = require('limiter').RateLimiter;
|
||
var limiter = new RateLimiter(1, 250);
|
||
|
||
limiter.removeTokens(1, function() {
|
||
callMyMessageSendingFunction(...);
|
||
});
|
||
```
|
||
|
||
The default behaviour is to wait for the duration of the rate limiting
|
||
that’s currently in effect before the callback is fired, but if you
|
||
pass in ```true``` as the third parameter, the callback will be fired
|
||
immediately with remainingRequests set to -1:
|
||
|
||
```javascript
|
||
var RateLimiter = require('limiter').RateLimiter;
|
||
var limiter = new RateLimiter(150, 'hour', true); // fire CB immediately
|
||
|
||
// Immediately send 429 header to client when rate limiting is in effect
|
||
limiter.removeTokens(1, function(err, remainingRequests) {
|
||
if (remainingRequests < 1) {
|
||
response.writeHead(429, {'Content-Type': 'text/plain;charset=UTF-8'});
|
||
response.end('429 Too Many Requests - your IP is being rate limited');
|
||
} else {
|
||
callMyMessageSendingFunction(...);
|
||
}
|
||
});
|
||
```
|
||
|
||
A synchronous method, tryRemoveTokens(), is available in both RateLimiter and TokenBucket. This will return immediately with a boolean value indicating if the token removal was successful.
|
||
```javascript
|
||
var RateLimiter = require('limiter').RateLimiter;
|
||
var limiter = new RateLimiter(10, 'second');
|
||
|
||
if (limiter.tryRemoveTokens(5))
|
||
console.log('Tokens removed');
|
||
else
|
||
console.log('No tokens removed');
|
||
```
|
||
|
||
To get the number of remaining tokens **outside** the `removeTokens`-callback
|
||
simply use the `getTokensRemaining`-method.
|
||
```javascript
|
||
var RateLimiter = require('limiter').RateLimiter;
|
||
var limiter = new RateLimiter(1, 250);
|
||
|
||
// returns 1 since we did not remove a token and our number of tokens per interval is 1
|
||
limiter.getTokensRemaining();
|
||
```
|
||
|
||
Using the token bucket directly to throttle at the byte level:
|
||
|
||
```javascript
|
||
var BURST_RATE = 1024 * 1024 * 150; // 150KB/sec burst rate
|
||
var FILL_RATE = 1024 * 1024 * 50; // 50KB/sec sustained rate
|
||
var TokenBucket = require('limiter').TokenBucket;
|
||
// We could also pass a parent token bucket in as the last parameter to
|
||
// create a hierarchical token bucket
|
||
var bucket = new TokenBucket(BURST_RATE, FILL_RATE, 'second', null);
|
||
|
||
bucket.removeTokens(myData.byteLength, function() {
|
||
sendMyData(myData);
|
||
});
|
||
```
|
||
|
||
## Additional Notes ##
|
||
|
||
Both the token bucket and rate limiter should be used with a message queue or
|
||
some way of preventing multiple simultaneous calls to removeTokens().
|
||
Otherwise, earlier messages may get held up for long periods of time if more
|
||
recent messages are continually draining the token bucket. This can lead to
|
||
out of order messages or the appearance of "lost" messages under heavy load.
|
||
|
||
## License ##
|
||
|
||
(The MIT License)
|
||
|
||
Copyright (c) 2013 John Hurliman. <jhurliman@jhurliman.org>
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining
|
||
a copy of this software and associated documentation files (the
|
||
'Software'), to deal in the Software without restriction, including
|
||
without limitation the rights to use, copy, modify, merge, publish,
|
||
distribute, sublicense, and/or sell copies of the Software, and to
|
||
permit persons to whom the Software is furnished to do so, subject to
|
||
the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be
|
||
included in all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|