[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);
}
}
}