middle
Что такое Rate Limiting и как его реализовать?
Rate Limiting (ограничение частоты запросов) — механизм защиты API от злоупотреблений и перегрузки путём ограничения количества запросов от клиента за определённый период времени.
Основные алгоритмы
| Алгоритм | Описание |
|---|---|
| Fixed Window | Подсчёт запросов в фиксированных временных интервалах (100 запросов в минуту) |
| Sliding Window | Более точный, учитывает запросы за последние N секунд |
| Token Bucket | Жетоны добавляются с постоянной скоростью. Каждый запрос забирает жетон. Нет жетонов — отказ |
| Leaky Bucket | Запросы обрабатываются с постоянной скоростью, избыточные ставятся в очередь |
HTTP-заголовки для Rate Limiting
Пример
HTTP/1.1 200 OK
X-RateLimit-Limit: 100 — максимум запросов
X-RateLimit-Remaining: 45 — осталось запросов
X-RateLimit-Reset: 1700000060 — время сброса (Unix timestamp)
HTTP/1.1 429 Too Many Requests
Retry-After: 30 — через сколько секунд повторить
Реализация в Spring с помощью Bucket4j
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-core</artifactId>
<version>8.7.0</version>
</dependency>
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String clientId = getClientId(request);
Bucket bucket = buckets.computeIfAbsent(clientId, this::createBucket);
ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
response.setHeader("X-RateLimit-Limit", "100");
response.setHeader("X-RateLimit-Remaining",
String.valueOf(probe.getRemainingTokens()));
if (probe.isConsumed()) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.setHeader("Retry-After",
String.valueOf(probe.getNanosToWaitForRefill() / 1_000_000_000));
response.getWriter().write("{\"error\": \"Превышен лимит запросов\"}");
}
}
private Bucket createBucket(String clientId) {
return Bucket.builder()
.addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))
.build();
}
private String getClientId(HttpServletRequest request) {
String apiKey = request.getHeader("X-API-Key");
return apiKey != null ? apiKey : request.getRemoteAddr();
}
}
Реализация через Spring Cloud Gateway
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
На собеседовании: нужно знать алгоритмы (Token Bucket самый популярный), HTTP-заголовки (429, Retry-After, X-RateLimit-*) и хотя бы один способ реализации. Частая ошибка — не упомянуть заголовок Retry-After для клиента.