达到比率时,Java Spring速率限制器阻止特定的持续时间



目前我有一个要求:为API应用速率限制器。如果此API每5秒调用100次以上,则API将被阻止10分钟。我不知道是否有任何java库可以满足这一要求。如果要求是";每5秒允许100个呼叫";或";每10分钟允许100个呼叫";那么我可以使用Bucket4j:

Bandwidth b = Bandwidth.classic(100, Refill.intervally(100, Duration.ofSeconds(5)));
//Bandwidth b = Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(10)));
Bucket bk = Bucket.builder().addLimit(b).build();
//then
if(bk.tryConsume(1)) {
//stuff
} else {
throw
}

或Resilence4j:

RateLimiterConfig config = RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(5))
.limitForPeriod(100)
.timeoutDuration(Duration.ofSeconds(1))
.build();
RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config);
RateLimiter rateLimiterWithCustomConfig = rateLimiterRegistry
.rateLimiter("name2", config);
CheckedRunnable restrictedCall = RateLimiter
.decorateCheckedRunnable(rateLimiterWithCustomConfig, this::doStuff);
//then
Try.run(restrictedCall).onFailure(throwable -> throw new RuntimeException());

但要求是";每5秒允许100个呼叫,如果更多,则阻止10分钟";。有任何lib可以工作吗?请给我一个解决这个案子的办法。谢谢

Bucket4j当前具有此功能,可以在特定时间段内阻塞bucket。(我可以在Resilence4j中说同样的话,但我不太确定!(

所以你应该手动维护黑名单(这将需要根据用例进行更复杂的代码改进!(

然而,我认为使用以下两个不同的限制在代码成本方面更好。(你可以让10分钟的时间保持一定的灵活性,并保持5秒的条件。(

Bandwidth bandwidth1 = Bandwidth.classic(
1200, Refill.intervally(
1200, Duration.ofMinutes(10)));
Bandwidth bandwidth2 = Bandwidth.classic(
100, Refill.intervally(
100, Duration.ofSeconds(5)));
Bucket bucket = Bucket.builder()
.addLimit(bandwidth1)
.addLimit(bandwidth2)
.build();

如果我们想让手动列入黑名单成为一个简单的例子

您可以记录被阻止的时间。然后,您可以编写另一个类来检查何时到达解锁日期:

import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Refill;
import java.time.Duration;
import java.time.LocalDateTime;
import static java.time.LocalDateTime.now;
public class BucketWithPenaltyTest {
public static void main(String[] args) throws InterruptedException {
Bandwidth bandwidth = Bandwidth.classic(
100, Refill.intervally(
100, Duration.ofSeconds(5)));
Bucket bucket = Bucket.builder()
.addLimit(bandwidth)
.build();
BucketWithPenalty bucketWithPenalty =
new BucketWithPenalty(bucket, 
Duration.ofMinutes(10).getSeconds());
while(true) {
Thread.sleep(400);
if (bucketWithPenalty.tryConsume(1)) {
System.out.println(String.format(
"[%s] %s", now(), 
"Ok."));
} else {
System.err.println(String.format(
"[%s] %s", now(), 
"Rate limit error!"));
}
}
}
}
import io.github.bucket4j.Bucket;
import java.time.LocalDateTime;
import static java.time.LocalDateTime.now;
public class BucketWithPenalty {
private static Bucket bucket;
private static Long blockingTimeSeconds;
private static LocalDateTime unblockingDate;
public BucketWithPenalty(Bucket bucket, Long blockingTimeSeconds) {
this.bucket = bucket;
this.blockingTimeSeconds = blockingTimeSeconds;
this.unblockingDate = now();
}
public boolean tryConsume(long numTokens) {
if (unblockingDate.isBefore(now())
&& bucket.tryConsume(numTokens)) {
unblockingDate = now();
return true;
} else {
if (!unblockingDate.isAfter(now())) {
unblockingDate = now().plusSeconds(blockingTimeSeconds);
}
return false;
}
}
}