Rate Limiting and Throttling in Rust: Taming Data Streams

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

When Rust Meets Overload: Why Does Your Service "Crash"?

\n\n

Imagine you've written a perfect high-load service in Rust. It flies, using all the advantages of zero-cost abstractions and memory ownership. But here's the trouble — one "greedy" client comes along and starts hammering your server with thousands of requests per second. What happens? Does the database crash? Does the queue choke? Or, even worse, do you get banned from the external API you're calling?

\n\n

In Rust, as in any other language, you can't get by without rate limiting and throttling. This isn't just a "best practice" — it's a matter of your application's survival in production. But Rust has one peculiarity: we love doing everything efficiently and from scratch. However, you won't have to reinvent the wheel — we have powerful crates (libraries) and elegant patterns.

\n\n

In this article, we'll break down how to properly implement request limiting in Rust, using both classic algorithms (Token Bucket) and modern asynchronous approaches. You'll learn how to protect your API from DDoS attacks, how not to exceed external service limits, and how to write your own throttler that will run faster than in Go or Python.

\n\n

Rate Limiting vs Throttling: What's the Difference?

\n\n

Before diving into code, let's once and for all clarify the terms. They are often confused, but the difference is critical.

\n\n
    \n
  • Rate Limiting — is a security policy. You say: "Client X can make no more than 100 requests per minute." If the limit is exceeded — you return 429 Too Many Requests. This protects your backend.
  • \n
  • Throttling — is a speed management mechanism. You decide at what speed to process requests to avoid overloading CPU, I/O, or the database. The client can send 1000 requests, but you will process them at 10 per second, queuing the rest.
  • \n
\n\n

In Rust, both approaches can be implemented using the same toolset, but with different logic. We'll cover both.

\n\n

Algorithms: Token Bucket and Leaky Bucket

\n\n

The most popular algorithms for rate limiting are Token Bucket and Leaky Bucket. They are simple as can be, but incredibly effective.

\n\n

Token Bucket

\n

Imagine a bucket into which N tokens drip every second. When a request arrives, it takes one token. If the bucket is empty — the request is rejected. Rust is ideal for implementing this algorithm thanks to atomic operations.

\n\n

Leaky Bucket

\n

Here, the bucket has a hole in the bottom through which a fixed number of requests leak out per second. Requests enter the bucket from the top. If the bucket overflows — excess requests are discarded. This algorithm is better for smoothing out peak loads.

\n\n

In Rust, we won't write these algorithms by hand (though it is educational). Instead, we'll use the governor crate — the gold standard for rate limiting in the Rust ecosystem.

\n\n

Practice: Installing Governor

\n\n

The governor crate is a fast, asynchronous, and thread-safe implementation of Token Bucket. It supports both synchronous and asynchronous modes, and works with tokio and async-std.

\n\n

Add to Cargo.toml:

\n\n
[dependencies]\ngovernor = "0.6"\ntokio = { version = "1", features = ["full"] }\n
\n\n

Now let's write a simple rate limiter for an HTTP server based on hyper or axum. For simplicity, we'll use axum.

\n\n
use axum::{\n    extract::Request,\n    http::StatusCode,\n    middleware::{self, Next},\n    response::Response,\n    routing::get,\n    Router,\n};\nuse governor::{DefaultDirectRateLimiter, Quota, RateLimiter};\n                

Blogs

Book Recommendations