我正在使用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 ms的值,例如:-
Boolean permitAcquired = RateLimitTest.rateLimiter.tryAcquire(1, 900, TimeUnit.MILLISECONDS);
我确实看到了RunTimeException,这意味着速率限制器按预期工作。我不明白为什么当超时时间大于等于1000ms时,限速器不强制限制。我做错了什么吗?
首先,最好记住tryAcquire
的作用(重点是我的):
在不超过指定的
timeout
的情况下,从该RateLimiter
获得给定数量的许可,或者立即返回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