如何避免 Guava RateLimiter 存储未使用的许可证?

问题描述

我想要一个速率限制器来避免溢出(在 T 持续时间内绝对不超过 N 个调用)。我使用 Guava 的 RateLimiter 进行了尝试,并创建了以下测试用例。

import com.google.common.util.concurrent.RateLimiter;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static java.time.Duration.between;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.assertj.core.api.Assertions.assertthat;
import static org.assertj.core.api.Assertions.assertthatCode;

class RateLimiterTest {

    @Test
    void test_guava_rate_limit() throws Exception {
        final int queryPerSecond = 10;
        final Random random = new Random(System.nanoTime());
        final RateLimiter rateLimiter = RateLimiter.create(queryPerSecond);

        final List<Instant> emitTimes = new ArrayList<>();
        final int maxSleepIntervalInNanoseconds = 1000 / queryPerSecond * 2;
        for (int i = 0; i < 10 * queryPerSecond; i++) {
            MILLISECONDS.sleep(random.nextInt(maxSleepIntervalInNanoseconds));
            rateLimiter.acquire();
            emitTimes.add(Instant.Now());
        }

        for (int i = 0; i < emitTimes.size() - queryPerSecond - 1; i++) {
            final Duration timetook = between(emitTimes.get(i),emitTimes.get(i + queryPerSecond + 1));
            assertthat(timetook)
                    .isGreaterThanorEqualTo(Duration.ofSeconds(1));
        }
    }
}

不幸的是,测试失败了。

阅读Guava的RateLimiter源码,我们有以下方法

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

  @VisibleForTesting
  static RateLimiter create(double permitsPerSecond,SleepingStopwatch stopwatch) {
    RateLimiter rateLimiter = new SmoothBursty(stopwatch,1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
  }

我怀疑这个问题是由于源代码中提到的“保存的许可证”造成的。认的 'maxBurstSeconds' 为 1s。我可以知道我是否可以避免它,或者将其减少到零?

提前感谢您的帮助。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)