Rate Limiting в Kotlin: библиотеки и реализация

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Rate Limiting в Kotlin: библиотеки, стратегии и практические примеры

Rate limiting (ограничение скорости запросов) — критически важный механизм для защиты API, микросервисов и веб-приложений от чрезмерной нагрузки, DDoS-атак и случайных всплесков трафика. В экосистеме Kotlin, работающей поверх JVM, существует несколько зрелых библиотек, которые позволяют гибко реализовать эту логику. В этой статье мы разберём три основных подхода и инструмента: Bucket4j, Resilience4j и встроенный RateLimiter из Guava.

1. Bucket4j — продвинутая реализация Token Bucket

Bucket4j — это Java-библиотека, идеально совместимая с Kotlin. Она реализует алгоритм Token Bucket (корзина токенов) и поддерживает как локальное, так и распределённое ограничение (через Redis, Hazelcast, Infinispan).

Ключевые возможности:

  • Настройка количества токенов и скорости их пополнения.
  • Поддержка «burst» (кратковременных всплесков) за счёт накопления токенов.
  • Работа в многопоточном окружении без блокировок.
  • Интеграция с Spring Boot через стартер bucket4j-spring-boot-starter.

Пример простого rate limiter в Kotlin:

import io.github.bucket4j.Bandwidth
import io.github.bucket4j.Bucket
import io.github.bucket4j.Refill
import java.time.Duration

fun main() {
    // 10 запросов в минуту, с возможностью накопления до 20 токенов
    val limit = Bandwidth.classic(20, Refill.greedy(10, Duration.ofMinutes(1)))
    val bucket = Bucket.builder()
        .addLimit(limit)
        .build()

    // Проверяем доступность токена
    if (bucket.tryConsume(1)) {
        println("✅ Запрос разрешён")
    } else {
        println("❌ Превышен лимит, попробуйте позже")
    }
}

Для распределённого сценария (например, несколько экземпляров микросервиса) используйте Bucket4j с Redis:

import io.github.bucket4j.distributed.proxy.ProxyManager
import io.github.bucket4j.redis.lettuce.Bucket4jLettuce
import io.lettuce.core.RedisClient

val redisClient = RedisClient.create("redis://localhost:6379")
val proxyManager: ProxyManager<String> = Bucket4jLettuce
    .builder(redisClient)
    .build()

val bucket = proxyManager.builder()
    .build("user:123", Bucket4jConfiguration(
        Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1)))
    ))

2. Resilience4j — RateLimiter как часть Circuit Breaker

Resilience4j — это популярная библиотека устойчивости для Java/Kotlin, включающая не только rate limiter, но и circuit breaker, retry, bulkhead. Её модуль RateLimiter работает на основе алгоритма Sliding Window (скользящее окно).

Особенности:

  • Лёгкая интеграция с корутинами Kotlin через suspend функции.
  • Настройка периода окна и лимита запросов.
  • Гибкая обработка отказов: возврат исключения, fallback или ожидание.

Пример с корутинами:

import io.github.resilience4j.ratelimiter.RateLimiter
import io.github.resilience4j.ratelimiter.RateLimiterConfig
import io.github.resilience4j.kotlin.ratelimiter.awaitRateLimiter
import java.time.Duration
import kotlinx.coroutines.runBlocking

val config = RateLimiterConfig.custom()
    .limitForPeriod(10)           // 10 запросов
    .limitRefreshPeriod(Duration.ofSeconds(1)) // за 1 секунду
    .timeoutDuration(Duration.ofMillis(500))   // таймаут ожидания
    .build()

val rateLimiter = RateLimiter.of("api-limiter", config)

suspend fun fetchData(): String {
    return awaitRateLimiter(rateLimiter) {
        // Ваш API-вызов
        "Данные получены"
    }
}

fun main() = runBlocking {
    val result = fetchData()
    println(result)
}

3. Google Guava RateLimiter — простота и скорость

Встроенный RateLimiter из библиотеки Guava — самый лёгкий вариант для простых сценариев. Он использует Token Bucket с плавным пополнением (smooth burst). Однако он не поддерживает распределённое состояние.

Пример использования:

import com.google.common.util.concurrent.RateLimiter

fun main() {
    val limiter = RateLimiter.create(5.0) // 5 запросов в секунду                    

Рекомендаций