【问题标题】:Guava ratelimiter doesn't work when timeout set to >= 1 second当超时设置为 >= 1 秒时,Guava ratelimiter 不起作用
【发布时间】:2021-05-28 18:32:40
【问题描述】:

我正在使用 Guava RateLimiter,并且一直在我的代码中创建 ratelimiter,如下所示。

public class RateLimitedCallable<T> implements Callable<T> {

    @Override
    public T call() {
        Boolean permitAcquired = RateLimitTest.rateLimiter.tryAcquire(1, 1000,
            TimeUnit.MILLISECONDS);

        if (permitAcquired) {
           // do stuff
        } else {
            throw new RuntimeException("Permit was not granted by RateLimiter");
        }
    }
}

public class RateLimitTest {

    public static final RateLimiter rateLimiter = RateLimiter.create(1.0);

    public void test() {
        RateLimitedCallable<String> callable = new RateLimitedCallable<>();
        callable.call();
        callable.call();
        callable.call();
        callable.call();
    }

    public static void main(String[] args) {
        RateLimitTest limiterTest = new RateLimitTest();
        limiterTest.test();
    } 

}

RuntimeException 永远不会被抛出。但是,如果我将超时更改为低于 1000 毫秒的值,例如:-

Boolean permitAcquired = RateLimitTest.rateLimiter.tryAcquire(1, 900, TimeUnit.MILLISECONDS);

我确实看到了 RunTimeException,这意味着速率限制器按预期工作。我不明白为什么当超时时间大于等于 1000 毫秒时速率限制器不强制执行限制。我做错了吗?

【问题讨论】:

    标签: java guava throttling rate-limiting


    【解决方案1】:

    首先,最好记住 tryAcquire 做了什么(强调我的):

    从这个RateLimiter获取给定数量的许可如果可以在不超过指定timeout的情况下获得,则立即返回false(无需等待)如果许可没有在超时到期之前被授予。

    在您的单线程示例中,它从不抛出任何异常是正常的,因为每个调用在获得许可之前都会等待大约一秒钟。所以这就是你的代码中发生的事情:

    1. 第一个调用知道它可以立即获得许可。因此它立即获得了许可证。
    2. 第一个调用完全完成后,第二个调用知道如果等待,它可以获得许可。所以它等待约 1 秒并获得许可。
    3. 第二个调用完全完成后,第三个调用知道如果等待就可以获得许可。所以它等待约 1 秒并获得许可。
    4. 第三次调用完全完成后,第四次调用知道如果等待就可以获得许可。所以它等待〜1s并获得许可
    5. 程序结束。

    现在尝试在多线程示例中使用它,您将开始看到多次失败和多次成功。因为他们都想同时获得许可证。

    1. 先得到是快乐的。
    2. 然后第二个知道它是否等待约 1 秒,它可以得到它,所以它一直等到它得到它。
    3. 第三个和第四个看到队列中已经有 2 个呼叫,并且知道他们必须等待 2 秒才能获得许可。所以他们放弃了,因为 2 秒大于您设置的 1 秒超时。

    所以基本上,只需使用多线程环境来测试它是否会发生。

      @Test
      void test() {
        var rateLimiter = RateLimiter.create(1.0);
        var stopwatch = Stopwatch.createStarted();
        var executor = Executors.newFixedThreadPool(4);
        for (var i = 0; i < 4; i++) {
          executor.submit(() -> {
            if (rateLimiter.tryAcquire(1, 1000, TimeUnit.MILLISECONDS)) {
              System.out.printf("Acquired after %s%n", stopwatch);
            } else {
              System.out.printf("Gave up trying to acquire after %s%n", stopwatch);
            }
          });
        }
        executor.shutdown();
        try {
          if (!executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
            executor.shutdownNow();
          }
        } catch (InterruptedException e) {
          executor.shutdownNow();
        }
      }
    

    这会导致

    Acquired after 12.76 ms
    Gave up trying to acquire after 12.41 ms
    Gave up trying to acquire after 12.43 ms
    Acquired after 1.004 s
    

    【讨论】:

      猜你喜欢
      • 2012-05-21
      • 1970-01-01
      • 2017-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-14
      • 1970-01-01
      相关资源
      最近更新 更多