LinkedIn

Java Ratelimiter - 초 당 처리량 조절하기

2021. 3. 1. 22:54 | 자바 개발자되기

Why?

  • 현재 담당하고 있는 서비스에서 API, DB 호출에 대한 초 당 처리량 조절 필요

 

Ratelimiter

Guava에서 제공하는 모듈로 초 당 처리량을 설정을 통하여 관리될 수 있도록 한다.

implementation("com.google.guava:guava")
  • gradle 의존성 추가

 

1. 인스턴스 생성

private final RateLimiter apiRateLimiter = RateLimiter.create(4);

public static RateLimiter create(double permitsPerSecond) {
    /*
     * The default RateLimiter configuration can save the unused permits of up to one second. This
     * is to avoid unnecessary stalls in situations like this: A RateLimiter of 1qps, and 4 threads,
     * all calling acquire() at these moments:
     *
     * T0 at 0 seconds
     * T1 at 1.05 seconds
     * T2 at 2 seconds
     * T3 at 3 seconds
     *
     * Due to the slight delay of T1, T2 would have to sleep till 2.05 seconds, and T3 would also
     * have to sleep till 3.05 seconds.
     */
    return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());
  }

 

  • 초 당 허용할 Task의 개수를 인자로 넘겨준다
public static RateLimiter create(double permitsPerSecond, Duration warmupPeriod) {
    return create(permitsPerSecond, toNanosSaturated(warmupPeriod), TimeUnit.NANOSECONDS);
  }

 

  • 원격 서버를 바로 호출하지 않고 웜업이 필요한 경우 이러한 생성자를 사용할 수도 있음

 

2. 획득

허용 acquire() 을 획득하여야만 Task를 처리할 수 있음

public double acquire() {
    return acquire(1);
}

@CanIgnoreReturnValue
public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
}

 

예약 reserve()

final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
}
  • 예약은 허가 횟수(permits)를 예약하고, 대기 시간을 반환함
  • 획득 acquire() 은 있지만 반납은 없다

 

주의 사항

내부적으로는 synchronized로 동작하여 권한 획득 시 대기 시간을 반환받고 대기 시간 이후 작업을 수행한다.

즉, 작업 권한을 부여받은 작업(Task)이 완료하여 종료되지 않더라도 다른 작업이 대기 시간 이후 작업을 수행할 수 있다.

Threadpool size를 고정적으로 사용하는 경우에는 큰 문제가 발생하지 않지만 동적으로 poolsize를 조절하는 newCachedThreadPool() 같은 경우 병목 현상이 발생한다면 최대 Integer.MAX_VALUE 만큼 thread가 생성될 수 있기에 유의하여 사용해야 한다.