diff --git a/src/main/java/hse/java/lectures/lesson7/limiter/RateLimiter.java b/src/main/java/hse/java/lectures/lesson7/limiter/RateLimiter.java index 08df1b1..fc63794 100644 --- a/src/main/java/hse/java/lectures/lesson7/limiter/RateLimiter.java +++ b/src/main/java/hse/java/lectures/lesson7/limiter/RateLimiter.java @@ -1,26 +1,35 @@ package hse.java.lectures.lesson7.limiter; import java.time.temporal.ChronoUnit; +import java.util.LinkedList; -/** - * Скользящий рейтлимитер: не больше заданного числа успешных {@link #check()} за последнюю секунду или минуту. - */ public class RateLimiter { - /** - * @param unit длина окна — только {@link ChronoUnit#SECONDS} или {@link ChronoUnit#MINUTES} - * (скользящее окно 1 секунда или 1 минута) - * @param maxRequests максимум успешных {@link #check()} за окно (должно быть > 0) - */ + private final long windowNanos; + private final int maxRequests; + private final LinkedList stamps = new LinkedList<>(); + public RateLimiter(ChronoUnit unit, int maxRequests) { - throw new UnsupportedOperationException("Not implemented"); + if (unit != ChronoUnit.SECONDS && unit != ChronoUnit.MINUTES) { + throw new IllegalArgumentException("bad unit"); + } + if (maxRequests < 1) { + throw new IllegalArgumentException("bad max"); + } + this.windowNanos = unit.getDuration().toNanos(); + this.maxRequests = maxRequests; } - /** - * Регистрирует попытку и возвращает, разрешена ли она в пределах лимита. - */ - public boolean check() { - throw new UnsupportedOperationException("Not implemented"); + public synchronized boolean check() { + long now = System.nanoTime(); + long border = now - windowNanos; + while (!stamps.isEmpty() && stamps.getFirst() <= border) { + stamps.removeFirst(); + } + if (stamps.size() < maxRequests) { + stamps.addLast(now); + return true; + } + return false; } - }