rate-limiter-flexible
Version:
Flexible API rate limiter backed by Redis for distributed node.js applications
88 lines (66 loc) • 2.54 kB
Markdown
## RateLimiterCluster
### Benchmark
Endpoint is pure NodeJS endpoint launched in `node:latest` and `redis:alpine` Docker containers with 4 workers
Endpoint is limited by `RateLimiterCluster` with config:
```javascript
new RateLimiterCluster({
points: 1000,
duration: 1,
});
```
By `bombardier -c 1000 -l -d 30s -r 2000 -t 5s http://127.0.0.1:8000`
Test with 1000 concurrent requests with maximum 2000 requests per sec during 30 seconds
```text
Statistics Avg Stdev Max
Reqs/sec 2024.57 234.52 2976.20
Latency 704.58us 165.65us 7.05ms
Latency Distribution
50% 669.00us
75% 843.00us
90% 1.02ms
95% 1.18ms
99% 1.60ms
HTTP codes:
1xx - 0, 2xx - 53458, 3xx - 0, 4xx - 6560, 5xx - 0
```
Cluster limiter at least twice faster than RateLimiterRedis.
### How it works
RateLimiterClusterMaster must be created in master process.
It receives messages from workers by IPC.
It creates necessary message handler, which process messages only from specific channel `rate_limiter_flexible`.
So if you use IPC for something else, it doesn't break anything.
```javascript
const { RateLimiterClusterMaster } = require('rate-limiter-flexible');
if (cluster.isMaster) {
new RateLimiterClusterMaster();
cluster.fork();
} else {
// ... your app ...
}
```
RateLimiterClusterMaster is Singleton.
It creates only one instance which process messages from all RateLimiterCluster created in workers.
RateLimiterCluster must be created with unique `keyPrefix`.
Every time RateLimiterCluster is created, it sends options to master.
Master instance creates specific rate limiter by `keyPrefix` and sends `init` command to worker
So there is a time when worker can't process requests until master sends `init` to worker.
It takes several milliseconds.
Worker is protected from loosing requests while it is instantiated.
It sets timer for 30 ms and waits `init` from master before processing requests
### Create several rate limiters
```javascript
const rateLimiter1 = new RateLimiterCluster({
keyPrefix: 'limiter1',
points: 100,
duration: 1,
});
const rateLimiter2 = new RateLimiterCluster({
keyPrefix: 'limiter2',
points: 10,
duration: 1,
execEvenly: true
});
```
If the second `keyPrefix` is the same `limiter1`, master doesn't create the second limiter.
This results to unexpected behaviour, because options from the first limiter are used.
**`keyPrefix` must be unique, if different options required**