Rate Limiting y Throttling en Kotlin: Cómo proteger tu API de sobrecargas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Cuando tu API grita "SOS": Introducción a Rate Limiting y Throttling

\n\n

Imagina: escribiste la aplicación Kotlin perfecta. Es rápida, elegante, usa corutinas y Flow. La publicaste en producción y, ¡bam! — un usuario "amable" (o peor, un atacante) comienza a golpear tu endpoint con miles de solicitudes por segundo. El servidor cae, la base de datos llora, y los demás usuarios reciben 503 Service Unavailable.

\n\n

¿Un dolor familiar? Precisamente para esto existen los mecanismos de rate limiting (limitación de velocidad) y throttling (estrangulamiento). En este artículo no solo hablaremos de teoría, sino que analizaremos con ejemplos prácticos cómo implementar estos patrones en Kotlin.

\n\n

Escribiremos código que realmente protegerá tu backend, ya sea una aplicación en Ktor, Spring Boot o corutinas puras. ¡Allá vamos!

\n\n

Rate Limiting vs Throttling: ¿Cuál es la diferencia (y por qué es importante)?

\n\n

Empecemos con lo básico para no confundirnos con los términos. Aunque estos conceptos se usan a menudo como sinónimos, hay un matiz entre ellos.

\n\n
    \n
  • Rate Limiting — es un límite estricto. "Solo puedes hacer 100 solicitudes por minuto. Después de la 101 — recibirás un 429 Too Many Requests y bloqueo hasta el próximo minuto". Se trata de un límite.
  • \n
  • Throttling — es una regulación más suave. "No puedes enviar solicitudes más rápido que 1 vez cada 200 ms. Si envías más rápido — las pondremos en cola o simplemente las ignoraremos". Se trata de la velocidad.
  • \n
\n\n

En el mundo de Kotlin, generalmente implementamos rate limiting a nivel de API (middleware) y throttling a nivel de operaciones específicas (por ejemplo, para no saturar un servidor externo con solicitudes).

\n\n

Algoritmos que todo desarrollador Kotlin debería conocer

\n\n

Antes de escribir código, elijamos la herramienta. Hay tres algoritmos clásicos que encajan perfectamente con la arquitectura de Kotlin.

\n\n

1. Token Bucket (Cubo de fichas) — El más popular

\n

Imagina un cubo al que cada segundo caen N fichas nuevas. El cubo tiene un volumen máximo (burst). Para procesar una solicitud, necesitas tomar una ficha. Si no hay fichas, la solicitud es rechazada.

\n

Ventajas: Permite pequeños picos de carga (bursts), es simple de implementar.

\n\n

2. Leaky Bucket (Cubo con fugas) — Suavizado de picos

\n

Un cubo con un agujero. Las solicitudes caen en el cubo, y el "agujero" las libera a una velocidad constante. Si el cubo se desborda, las solicitudes se descartan.

\n

Ventajas: Ideal para nivelar la carga, no permite picos.

\n\n

3. Sliding Window (Ventana deslizante) — Precisión sin tirones

\n

En lugar de límites rígidos (como en "1 minuto"), observamos un intervalo de tiempo deslizante. Más complejo de implementar, pero refleja con mayor precisión la carga real.

\n\n

Para proyectos Kotlin, Token Bucket es el punto medio dorado entre simplicidad y eficiencia. Ese es el que implementaremos.

\n\n

Escribiendo código: Rate Limiter en Kotlin desde cero (sin librerías)

\n\n

Vamos a crear un Rate Limiter simple pero funcional, usando corutinas y Mutex. Esta será una versión thread-safe de Token Bucket.

\n\n
import kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlin.time.Duration\nimport kotlin.time.TimeSource\n\nclass TokenBucketRateLimiter(\n    private val maxTokens: Int,\n    private val refillRate: Long, // tokens por segundo\n    private val timeSource: TimeSource = TimeSource.Monotonic\n) {\n    private var tokens: Long = maxTokens.toLong()\n    private var lastRefillTimestamp: Long = timeSource.markNow().elapsedNow().inWholeMilliseconds\n    private val mutex = Mutex()\n\n    suspend fun tryAcquire(): Boolean {\n        mutex.withLock {\n            refillTokens()\n            return if (tokens > 0) {\n                tokens--\n                true\n            } else {\n                

Blogs

Book Recommendations