-
-
Notifications
You must be signed in to change notification settings - Fork 163
RateLimiterQueue
Limit number of available tokens per interval with FIFO (first in, first out
) queue on single server or distributed environment.
It strictly respects queue order with Memory and Cluster limiters only.
const http = require('http');
const express = require('express');
const {RateLimiterMemory} = require('rate-limiter-flexible');
const limiterFlexible = new RateLimiterMemory({
points: 2,
duration: 1,
});
const limiter = new RateLimiterQueue(limiterFlexible, {
maxQueueSize: 100,
});
const app = express();
app.get('/', async (req, res) => {
try {
const remainingTokens = await limiter.removeTokens(1)
res.end(remainingTokens)
} catch(err) {
if (err instanceof Error) {
res.status(400).end()
} else {
res.status(429).send('Too Many Requests');
}
}
});
const server = http.createServer(app);
server.listen(3002, () => {
console.log('RateLimiterQueue service started');
});
There is RateLimiterMemory
instance created with 2 points
available per one second duration
in this example. (points and tokens are the same thing in this case).
maxQueueSize
option is set to 100. Default value is 4294967295
(2 ^ 32 - 1)
- If the queue is full and another request tries to remove token(s),
removeTokens
immediately rejected withRateLimiterQueueError
. -
removeTokens
can be also rejected withRateLimiterQueueError
, if application tries to remove more tokens than allowed per interval. - If you use one of store limiter like Redis, MongoDB or any other, it may be rejected with error from store.
RateLimiterQueueError
can be got from components of rate-limiter-flexible
:
const RateLimiterQueueError = require('rate-limiter-flexible/lib/component/RateLimiterQueueError')
const Redis = require('ioredis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
const redisClient = new Redis({ enableOfflineQueue: false });
const rlRedis = new RateLimiterRedis({
storeClient: redisClient,
points: 2, // Number of tokens
duration: 5, // Per 5 second interval
});
const rlQueue = new RateLimiterQueue(rlRedis);
rlQueue.getTokensRemaining()
.then((tokensRemaining) => { res.end(tokensRemaining) })
.catch((errFromStore) => { res.status(500).end() })
Migration from limiter
This RateLimiterQueue
provides the same features as rate limiter from limiter
package.
Advantages in comparison:
- Works in multi-server scenario with any store limiter like Redis, MongoDB or any other from
rate-limiter-flexible
. - Respects queue order with Memory and Cluster limiters.
- Works on top of native promises.
Example of migration:
var RateLimiter = require('limiter').RateLimiter;
var limiter = new RateLimiter(150, 'hour');
limiter.removeTokens(1, function(err, remainingRequests) {
callMyRequestSendingFunction(...);
});
Should be changed to:
const {RateLimiterMemory, RateLimiterQueue} = require('rate-limiter-flexible');
const limiterFlexible = new RateLimiterMemory({
points: 150,
duration: 60 * 60, // hour
});
const limiter = new RateLimiterQueue(limiterFlexible);
app.get('/', async (req, res) => {
const remainingTokens = await limiter.removeTokens(1);
callMyRequestSendingFunction(...);
})
Scroll top to read more.
Get started
Middlewares and plugins
Migration from other packages
Limiters:
- Redis
- Memory
- DynamoDB
- Prisma
- MongoDB (with sharding support)
- PostgreSQL
- MySQL
- BurstyRateLimiter
- Cluster
- PM2 Cluster
- Memcached
- RateLimiterUnion
- RateLimiterQueue
Wrappers:
- RLWrapperBlackAndWhite Black and White lists
Knowledge base:
- Block Strategy in memory
- Insurance Strategy
- Comparative benchmarks
- Smooth out traffic peaks
-
Usage example
- Minimal protection against password brute-force
- Login endpoint protection
- Websocket connection prevent flooding
- Dynamic block duration
- Different limits for authorized users
- Different limits for different parts of application
- Block Strategy in memory
- Insurance Strategy
- Third-party API, crawler, bot rate limiting