[java] N 초 내에 M 요청에 대한 제한 메소드 호출

N 초 안에 최대 M 호출까지 일부 메소드 실행을 조절하는 구성 요소 / 클래스가 필요합니다 (또는 ms 또는 nano는 중요하지 않습니다).

즉, N 초 슬라이딩 창에서 내 메소드가 M 번 이상 실행되지 않아야합니다.

기존 수업을 모르는 경우 솔루션 / 아이디어를 구현하는 방법을 자유롭게 게시하십시오.



답변

고정 크기 M의 타임 스탬프 링 버퍼 를 사용합니다 . 메서드가 호출 될 때마다 가장 오래된 항목을 확인하고 과거에 N 초 미만인 경우 다른 항목을 실행하고 추가합니다. 시차.


답변

나를 위해 즉시 사용 된 것은 Google Guava RateLimiter 입니다.

// Allow one request per second
private RateLimiter throttle = RateLimiter.create(1.0);

private void someMethod() {
    throttle.acquire();
    // Do something
}


답변

구체적으로,을 사용하여이를 구현할 수 있어야합니다 DelayQueue. M Delayed지연 시간이 0으로 설정된 인스턴스를 사용 하여 큐를 초기화하십시오 . 메소드에 대한 요청이 들어 오면 take토큰은 조절 요구 사항이 충족 될 때까지 메소드를 차단합니다. 토큰을 가져 오면 add지연 시간이 대기열에있는 새 토큰입니다 N.


답변

토큰 버킷 알고리즘을 읽으십시오 . 기본적으로 토큰이 들어있는 버킷이 있습니다. 메소드를 실행할 때마다 토큰을 가져옵니다. 더 이상 토큰이 없으면 토큰을 얻을 때까지 차단합니다. 한편, 일정한 간격으로 토큰을 보충하는 외부 행위자가 있습니다.

이 작업을 수행하는 라이브러리를 알지 못합니다. 이 논리를 코드에 쓰거나 AspectJ를 사용하여 동작을 추가 할 수 있습니다.


답변

분산 시스템에서 작동하는 Java 기반 슬라이딩 윈도우 속도 제한 기가 필요한 경우 https://github.com/mokies/ratelimitj 프로젝트를 살펴볼 수 있습니다 .

요청을 분당 50 개로 제한하는 Redis 지원 구성은 다음과 같습니다.

import com.lambdaworks.redis.RedisClient;
import es.moki.ratelimitj.core.LimitRule;

RedisClient client = RedisClient.create("redis://localhost");
Set<LimitRule> rules = Collections.singleton(LimitRule.of(1, TimeUnit.MINUTES, 50)); // 50 request per minute, per key
RedisRateLimit requestRateLimiter = new RedisRateLimit(client, rules);

boolean overLimit = requestRateLimiter.overLimit("ip:127.0.0.2");

Redis 구성에 대한 자세한 내용 은 https://github.com/mokies/ratelimitj/tree/master/ratelimitj-redis를 참조 하십시오 .


답변

응용 프로그램에 따라 다릅니다.

하는 경우 상상 여러 스레드가 몇 가지 할 수있는 토큰을 원하는 전 세계적으로 속도 – 제한 조치허용 버스트 없음을 즉, 10 초 당 10 개 조치를 제한 할 (하지만 당신은 10 개 행동이 첫 번째, 두 번째에서 일하고자 다음 남아 있지 않습니다 9 초 중지).

DelayedQueue의 단점은 스레드 요청 토큰이 요청을 이행하는 순서가 아닐 수도 있다는 단점이 있습니다. 토큰을 기다리는 여러 스레드가 차단 된 경우 다음 스레드를 사용할 스레드가 확실하지 않습니다. 내 견해로는 스레드가 영원히 대기 할 수도 있습니다.

한 가지 해결책은 두 개의 연속 작업 사이에 최소 시간 간격을두고 요청한 순서와 동일한 순서로 작업을 수행하는 것입니다.

구현은 다음과 같습니다.

public class LeakyBucket {
    protected float maxRate;
    protected long minTime;
    //holds time of last action (past or future!)
    protected long lastSchedAction = System.currentTimeMillis();

    public LeakyBucket(float maxRate) throws Exception {
        if(maxRate <= 0.0f) {
            throw new Exception("Invalid rate");
        }
        this.maxRate = maxRate;
        this.minTime = (long)(1000.0f / maxRate);
    }

    public void consume() throws InterruptedException {
        long curTime = System.currentTimeMillis();
        long timeLeft;

        //calculate when can we do the action
        synchronized(this) {
            timeLeft = lastSchedAction + minTime - curTime;
            if(timeLeft > 0) {
                lastSchedAction += minTime;
            }
            else {
                lastSchedAction = curTime;
            }
        }

        //If needed, wait for our time
        if(timeLeft <= 0) {
            return;
        }
        else {
            Thread.sleep(timeLeft);
        }
    }
}


답변

요청한 내용이 아니지만 ThreadPoolExecutorN 초 안에 M 요청 대신 M 개의 동시 요청을 처리하도록 설계된도 유용 할 수 있습니다.