Designing an API rate limiter is crucial for maintaining system performance and preventing abuse. Here’s a high-level overview of how you can design a scalable API rate limiter:
Components:
- Rate Limiter Middleware: This sits between the client and the server, handling rate limiting logic.
- Counter Storage: Stores the count of requests made by each user or IP address. In-memory data stores like Redis are commonly used for fast access.
- Rate Limiting Algorithm: Determines how to count and limit requests (e.g., token bucket, leaky bucket).
High-Level Design:
- Client Request: The client sends a request to the API endpoint.
- Middleware Check: The rate limiter middleware checks the counter for the client’s IP/user ID.
- Counter Update: If the counter is below the limit, the middleware allows the request and increments the counter. If the limit is reached, the middleware blocks or throttles the request.
- Counter Expiry: The counter expires after a set time period, allowing the client to make requests again.
Example Implementation:
Here’s a simplified example using Redis for counter storage:
#include <iostream>
#include <string>
#include <unordered_map>
#include <chrono>
class RateLimiter {
public:
RateLimiter(int limit, int timeWindow) : limit(limit), timeWindow(timeWindow) {}
bool allowRequest(const std::string& clientId) {
auto now = std::chrono::steady_clock::now();
auto& counter = counters[clientId];
if (now - counter.lastRequestTime > timeWindow) {
counter.count = 0;
}
if (counter.count < limit) {
counter.count++;
counter.lastRequestTime = now;
return true;
} else {
return false;
}
}
private:
struct Counter {
int count = 0;
std::chrono::steady_clock::time_point lastRequestTime;
};
int limit;
int timeWindow;
std::unordered_map<std::string, Counter> counters;
};
int main() {
RateLimiter rateLimiter(10, std::chrono::seconds(60));
std::string clientId = "client123";
for (int i = 0; i < 15; ++i) {
if (rateLimiter.allowRequest(clientId)) {
std::cout << "Request allowed" << std::endl;
} else {
std::cout << "Request denied" << std::endl;
}
}
return 0;
}
Explanation:
- RateLimiter Class: Manages counters for each client.
- allowRequest Method: Checks if the client has exceeded the rate limit.
- Counter Expiry: Resets the counter after the time window expires.