Rate Limiting in Scala

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

Our rate limiting example demonstrates how to control resource utilization and maintain quality of service. Scala elegantly supports rate limiting with actors and schedulers.

import akka.actor.{Actor, ActorSystem, Props}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

class RateLimiter extends Actor {
  private val limiter = context.system.scheduler.scheduleWithFixedDelay(0.milliseconds, 200.milliseconds, self, "tick")

  def receive = {
    case "tick" =>
      println(s"request ${System.currentTimeMillis()}")
    case _ =>
  }
}

object RateLimiting extends App {
  val system = ActorSystem("RateLimitingSystem")
  val rateLimiter = system.actorOf(Props[RateLimiter], "rateLimiter")

  // Simulate 5 requests
  for (i <- 1 to 5) {
    rateLimiter ! "request"
  }

  // Allow some time for processing
  Thread.sleep(2000)

  // Demonstrate bursty rate limiting
  val burstyLimiter = system.actorOf(Props(new Actor {
    private var tokens = 3
    private val replenishInterval = 200.milliseconds

    private val replenisher = context.system.scheduler.scheduleWithFixedDelay(0.milliseconds, replenishInterval, self, "replenish")

    def receive = {
      case "request" if tokens > 0 =>
        tokens -= 1
        println(s"bursty request ${System.currentTimeMillis()}")
      case "replenish" if tokens < 3 =>
        tokens += 1
      case _ =>
    }
  }), "burstyLimiter")

  // Simulate 5 bursty requests
  for (i <- 1 to 5) {
    burstyLimiter ! "request"
  }

  // Allow some time for processing
  Thread.sleep(2000)

  system.terminate()
}

In this Scala version, we use Akka actors to implement rate limiting. The RateLimiter actor uses a scheduler to receive a message every 200 milliseconds, simulating the rate limiting behavior.

For the bursty rate limiting, we create another actor that maintains a token bucket. It starts with 3 tokens and replenishes one token every 200 milliseconds, up to a maximum of 3 tokens. This allows for short bursts of requests while preserving the overall rate limit.

Running our program, we would see output similar to this:

request 1623456789000
request 1623456789200
request 1623456789400
request 1623456789600
request 1623456789800
bursty request 1623456791000
bursty request 1623456791000
bursty request 1623456791000
bursty request 1623456791200
bursty request 1623456791400

In the output, we can see that the first set of requests are handled once every ~200 milliseconds as desired. For the bursty requests, the first three are handled immediately due to the available tokens, and the remaining two are handled with ~200ms delays as tokens are replenished.

This example demonstrates how Scala and Akka can be used to implement both simple and bursty rate limiting schemes, providing flexible control over resource utilization in your applications.