Rate Limiting in Perl
Here’s the translation of the Go rate limiting example to Perl, formatted in Markdown suitable for Hugo:
Rate limiting is an important mechanism for controlling resource utilization and maintaining quality of service. Perl supports rate limiting through a combination of its threading capabilities and time management functions.
use strict;
use warnings;
use threads;
use Thread::Queue;
use Time::HiRes qw(time sleep);
# 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 queue of the same name.
my $requests = Thread::Queue->new();
for my $i (1..5) {
$requests->enqueue($i);
}
$requests->end();
# This limiter function will sleep for 200 milliseconds
# between each request. This is the regulator in our
# rate limiting scheme.
sub limiter {
while (defined(my $req = $requests->dequeue())) {
sleep(0.2);
printf("request %d %f\n", $req, time());
}
}
# By calling the limiter function for each request,
# we limit ourselves to 1 request every 200 milliseconds.
limiter();
# 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
# using a bursty limiter.
my $bursty_requests = Thread::Queue->new();
for my $i (1..5) {
$bursty_requests->enqueue($i);
}
$bursty_requests->end();
# This bursty_limiter allows bursts of up to 3 events.
sub bursty_limiter {
my $burst = 3;
my $last_time = time();
while (defined(my $req = $bursty_requests->dequeue())) {
my $current_time = time();
my $time_passed = $current_time - $last_time;
if ($time_passed < 0.2 && $burst > 0) {
$burst--;
} elsif ($time_passed < 0.2) {
sleep(0.2 - $time_passed);
$burst = 2;
} else {
$burst = 3;
}
$last_time = time();
printf("request %d %f\n", $req, $last_time);
}
}
# Now simulate 5 more incoming requests. The first
# 3 of these will benefit from the burst capability
# of bursty_limiter.
bursty_limiter();
Running our program we see the first batch of requests handled once every ~200 milliseconds as desired.
$ perl rate_limiting.pl
request 1 1621234567.123456
request 2 1621234567.323456
request 3 1621234567.523456
request 4 1621234567.723456
request 5 1621234567.923456
For the second batch of requests we serve the first 3 immediately because of the burstable rate limiting, then serve the remaining 2 with ~200ms delays each.
request 1 1621234568.123456
request 2 1621234568.123556
request 3 1621234568.123656
request 4 1621234568.323756
request 5 1621234568.523856
In this Perl implementation, we use the Thread::Queue
module to simulate channels, and the Time::HiRes
module for high-resolution time functions. The sleep()
function is used to implement the rate limiting delay.
The bursty limiter is implemented using a counter ($burst
) that allows up to 3 quick requests before enforcing the rate limit. This provides a similar functionality to the buffered channel in the original Go example.
Note that Perl’s threading model is different from Go’s goroutines, so this implementation uses a single-threaded approach for simplicity. In a real-world scenario, you might want to use Perl’s threading capabilities to handle requests concurrently.