Python token bucket rate limiting

Zachary 46 Published: 11/27/2024

Python token bucket rate limiting

Python provides an excellent library for implementing a token bucket rate limiter, which is designed to regulate the flow of tokens (or requests) within a certain time interval. This type of mechanism is particularly useful when dealing with APIs that require throttling or rate limiting.

The token bucket rate limiter works by maintaining a "bucket" that contains a limited number of tokens, each representing a single unit of request or action. When a request is made, the limiter checks if there are sufficient tokens available in the bucket to fulfill the request. If there are, it grants the request and refills the bucket at a specified rate.

Here's an example implementation using Python's queue module:

import queue

import time

class TokenBucket:

def init(self, capacity, refill_rate):

self.capacity = capacity

self.refill_rate = refill_rate

self.tokens = 0

self.last_refill_time = time.time()

def add_tokens(self, num_tokens):

self.tokens += num_tokens

def get_tokens(self):

return self.tokens

def check_token_availability(self, num_tokens_needed):

if self.tokens >= num_tokens_needed:

self.tokens -= num_tokens_needed

return True

else:

refill_time = time.time() - self.last_refill_time

refill_tokens = int(refill_time * self.refill_rate)

self.add_tokens(refill_tokens)

if self.tokens >= num_tokens_needed:

self.tokens -= num_tokens_needed

return True

else:

return False

Example usage:

bucket = TokenBucket(capacity=5, refill_rate=1)

def make_request():

num_tokens_needed = 2

if bucket.check_token_availability(num_tokens_needed):

print(f"Request granted! {num_tokens_needed} tokens consumed.")

else:

print("Rate limit exceeded. Please try again later.")

while True:

for _ in range(10): # simulate multiple requests

make_request()

time.sleep(0.1) # simulate some processing time

In this example, the TokenBucket class represents the rate limiter. It maintains a "bucket" of tokens (represented by the tokens attribute), which is initially empty. The add_tokens method allows you to add tokens to the bucket, and the get_tokens method returns the current number of tokens.

The check_token_availability method is where the magic happens. It checks if there are sufficient tokens available in the bucket for a given request. If there are enough tokens, it grants the request by subtracting the requested amount from the bucket's token count. If not, it calculates how many refill tokens would be needed based on the refill rate and the time elapsed since the last refill.

The make_request function is an example of how you might use this rate limiter in a real-world scenario. It attempts to make a request by checking if there are sufficient tokens available. If so, it prints a message indicating that the request was granted, and then subtracts the requested amount of tokens from the bucket. If not, it prints a message indicating that the rate limit was exceeded.

In this example, the refill rate is set to 1 token per second, and the capacity is set to 5 tokens. This means that you can make up to 5 requests within a single second without running into any issues. The make_request function simulates multiple requests being made over time, with some processing time in between each request.

By adjusting the refill rate, capacity, and other parameters of the token bucket rate limiter, you can fine-tune your rate limiting strategy to suit the specific needs of your application or API.

Python decorator rate limit

I'd be happy to help you with that.

Rate Limiting with Python Decorators

Rate limiting is a common technique used to prevent abuse and excessive usage of an API or service. In this scenario, we'll create a simple decorator in Python that limits the number of times a function can be called within a specified time frame.

Why Use Decorators?

Decorators are a powerful feature in Python that allows you to modify the behavior of functions without changing their underlying implementation. They're particularly useful for implementing rate limiting, logging, caching, and other forms of aspect-oriented programming.

Creating the Rate Limiter Decorator

Here's an example decorator that limits the number of times a function can be called within a specified time frame (e.g., 10 requests per minute):

import time

from functools import wraps

def rate_limit(limit_per_minute, sleep_time=1.0):

def decorator(func):

@wraps(func)

def wrapper(*args, **kwargs):

now = int(time.time() / 60) # Convert to minutes

key = f"{func.name}-{now}"

if key in rate_limit_cache:

last_call_time = rate_limit_cache[key]

if (time.time() - last_call_time) < limit_per_minute:

time.sleep(sleep_time)

return "Rate limit exceeded. Try again later."

else:

rate_limit_cache[key] = now

return func(*args, **kwargs)

return wrapper

rate_limit_cache = {}

return decorator

@rate_limit(10) # Limit to 10 requests per minute

def example_function():

print("Hello from example function!")

How It Works

When you decorate a function with @rate_limit, the decorator creates a cache of the last time each decorated function was called. When the function is called, it checks if there's a pending request within the limit (e.g., 10 requests per minute). If there is, it will sleep for the specified amount of time (sleep_time) before allowing the request to proceed.

Advantages

This rate limiter decorator provides several advantages:

Easy to implement: The decorator does all the heavy lifting for you, making it simple to integrate with your existing code. Flexible: You can adjust the rate limiting parameters (e.g., limit_per_minute and sleep_time) to suit your specific needs. Thread-safe: The cache is thread-safe, ensuring that multiple threads won't interfere with each other's rate limiting.

Limitations

Keep in mind that this decorator is not foolproof. A determined attacker could still exploit the system by:

Spreading requests across multiple threads or processes, thereby evading the rate limiter. Using custom HTTP headers or query parameters to bypass the rate limiter.

To mitigate these risks, consider implementing additional security measures, such as IP blocking or CAPTCHA verification.

I hope this helps you implement a basic rate limiting decorator in Python!