当超时设置为 >= 1 秒时,Guava ratelimiter 不起作用
Guava ratelimiter doesn't work when timeout set to >= 1 second
我正在使用 Guava 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 毫秒时,速率限制器不强制执行限制。我做错了什么吗?
首先,最好记住 tryAcquire
的作用(强调我的):
Acquires the given number of permits from this RateLimiter
if it can be obtained without exceeding the specified timeout
, or returns false
immediately (without waiting) if the permits would not have been granted before the timeout expired.
在您的单线程示例中,它从不抛出任何异常是正常的,因为每个调用在获得许可之前大约等待一秒钟。所以这是您的代码中发生的事情:
- 第一次调用就知道可以马上拿到permit。所以它立即获得了许可证。
- 第一次调用完全完成后,第二次调用知道如果等待就可以获得许可。所以它等待 ~1s 并获得许可。
- 第二次调用完全完成后,第三次调用知道如果等待就可以获得许可。所以它等待 ~1s 并获得许可。
- 第三次调用完全完成后,第四次调用知道如果等待就可以得到许可。所以它等待 ~1s 并获得许可
- 程序结束。
现在尝试在多线程示例中使用它,您将开始看到几次失败和几次成功。因为他们都想同时拿到permit
- 先得者喜。
- 然后第二个知道它是否等待 ~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
我正在使用 Guava 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 毫秒时,速率限制器不强制执行限制。我做错了什么吗?
首先,最好记住 tryAcquire
的作用(强调我的):
Acquires the given number of permits from this
RateLimiter
if it can be obtained without exceeding the specifiedtimeout
, or returnsfalse
immediately (without waiting) if the permits would not have been granted before the timeout expired.
在您的单线程示例中,它从不抛出任何异常是正常的,因为每个调用在获得许可之前大约等待一秒钟。所以这是您的代码中发生的事情:
- 第一次调用就知道可以马上拿到permit。所以它立即获得了许可证。
- 第一次调用完全完成后,第二次调用知道如果等待就可以获得许可。所以它等待 ~1s 并获得许可。
- 第二次调用完全完成后,第三次调用知道如果等待就可以获得许可。所以它等待 ~1s 并获得许可。
- 第三次调用完全完成后,第四次调用知道如果等待就可以得到许可。所以它等待 ~1s 并获得许可
- 程序结束。
现在尝试在多线程示例中使用它,您将开始看到几次失败和几次成功。因为他们都想同时拿到permit
- 先得者喜。
- 然后第二个知道它是否等待 ~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