Author: June Hong

  • Designing an API rate limiter

    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:

    1. Rate Limiter Middleware: This sits between the client and the server, handling rate limiting logic.
    2. 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.
    3. Rate Limiting Algorithm: Determines how to count and limit requests (e.g., token bucket, leaky bucket).

    High-Level Design:

    1. Client Request: The client sends a request to the API endpoint.
    2. Middleware Check: The rate limiter middleware checks the counter for the client’s IP/user ID.
    3. 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.
    4. 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.