Rate Limiting in Rust

Here’s the translation of the Go rate limiting example to Rust, formatted in Markdown for Hugo:

use std::time::{Duration, Instant};
use std::thread;
use std::sync::mpsc;

fn main() {
    // First we'll look at basic rate limiting. Suppose
    // we want to limit our handling of incoming requests.
    // We'll serve these requests off a channel of the
    // same name.
    let (tx, rx) = mpsc::channel();
    for i in 1..=5 {
        tx.send(i).unwrap();
    }
    drop(tx);

    // This `limiter` will receive a value every 200 milliseconds.
    // This is the regulator in our rate limiting scheme.
    let limiter = std::iter::repeat_with(|| {
        thread::sleep(Duration::from_millis(200));
        Instant::now()
    });

    // By blocking on a receive from the `limiter` iterator
    // before serving each request, we limit ourselves to
    // 1 request every 200 milliseconds.
    for (req, _) in rx.iter().zip(limiter) {
        println!("request {} {:?}", req, Instant::now());
    }

    // We may want to allow short bursts of requests in
    // our rate limiting scheme while preserving the
    // overall rate limit. We can accomplish this by
    // buffering our limiter channel. This `bursty_limiter`
    // channel will allow bursts of up to 3 events.
    let (bursty_tx, bursty_rx) = mpsc::sync_channel(3);

    // Fill up the channel to represent allowed bursting.
    for _ in 0..3 {
        bursty_tx.send(Instant::now()).unwrap();
    }

    // Every 200 milliseconds we'll try to add a new
    // value to `bursty_tx`, up to its limit of 3.
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_millis(200));
            bursty_tx.send(Instant::now()).unwrap();
        }
    });

    // Now simulate 5 more incoming requests. The first
    // 3 of these will benefit from the burst capability
    // of `bursty_rx`.
    let (bursty_req_tx, bursty_req_rx) = mpsc::channel();
    for i in 1..=5 {
        bursty_req_tx.send(i).unwrap();
    }
    drop(bursty_req_tx);

    for req in bursty_req_rx {
        bursty_rx.recv().unwrap();
        println!("request {} {:?}", req, Instant::now());
    }
}

This Rust code demonstrates rate limiting using channels and threads. Here’s a breakdown of the implementation:

  1. We first create a basic rate limiter using a channel and an iterator that produces values every 200 milliseconds.

  2. We then implement a bursty rate limiter using a synchronous channel with a capacity of 3, allowing short bursts of requests while maintaining the overall rate limit.

  3. The mpsc::channel() and mpsc::sync_channel() functions are used to create channels for communication between threads.

  4. We use thread::sleep() to simulate time delays and Instant::now() to get the current time for logging.

  5. The thread::spawn() function is used to create a new thread that continuously adds values to the bursty limiter channel.

Running this program will show the first batch of requests handled once every ~200 milliseconds as desired. For the second batch of requests, we’ll see the first 3 served immediately due to the burstable rate limiting, then the remaining 2 with ~200ms delays each.

This Rust implementation provides similar functionality to the original example, demonstrating rate limiting concepts in a language with different concurrency primitives.