问题描述
我正在使用 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,这意味着 ratelimiter 按预期工作。我不明白为什么当超时时间大于等于 1000 毫秒时 ratelimiter 不强制执行限制。我做错了什么吗?
解决方法
首先,最好记住 tryAcquire
的作用(强调我的):
从此RateLimiter
获取给定数量的许可如果可以在不超过指定的timeout
的情况下获得,或立即返回false
(无需等待)如果在超时到期之前没有授予许可证。
在您的单线程示例中,它从不抛出任何异常是正常的,因为每个调用在获得许可之前大约等待一秒钟。所以这是您的代码中发生的事情:
- 第一次调用就知道它可以立即获得许可证。所以它立即获得了许可证。
- 在第一个调用完全完成后,第二个调用知道如果它等待就可以获得许可。所以它等待 ~1s 并获得许可。
- 在第二次调用完全完成后,第三次调用知道如果它等待,它可以得到许可。所以它等待 ~1s 并获得许可。
- 在第三次调用完全完成后,第四次调用知道如果它等待,它可以得到许可。所以它等待~1s并获得许可
- 节目结束。
现在尝试在多线程示例中使用它,您将开始看到几次失败和几次成功。因为他们都想同时拿到许可证。
- 第一个获得的是快乐。
- 然后第二个知道它是否等待了大约 1 秒,它可以得到它,所以它一直等到它得到它。
- 第三个和第四个看到队列中已经有 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