您当前的位置:首页 >> 传感器
传感器

Java培训-怎样通过 Bucket4j 共享速率限制

发布时间:2025-11-01

未能构建 Token Bucket 的准确性签约。

4.通过 Bucket4j 构建 Rate-Limiter

让我们考量一下 Bucket4j 奎构建的 Token Bucket 演算法。

Bucket4j 是 Java 世界性中所常用构建频率约束的系统的最流行起来的奎。每个月,Bucket4j 从 Maven Central 下载多达 200,000 次,并值得注意在 GitHub 上的 3500 个相反项中所。

让我们考量几个最简单的值得注意(我们将适用 Maven 作为软件数据分析制度和忽略工具)。

对于第一个,我们须要在 pom.xml 中所缓冲一个相反项:

com.github.vladimir-bukhtoyarov

bucket4j-core

7.0.0

创建者 Example.java:

import io.github.bucket4j.Bandwidth;

import io.github.bucket4j.Bucket;

import io.github.bucket4j.Bucket4j;

import io.github.bucket4j.ConsumptionProbe;

import java.time.Duration;

public class Example {

public static void main(String args[]) {

//Create the Bandwidth to set the rule - one token per minute

Bandwidth oneCosumePerMinuteLimit = Bandwidth.simple(1, Duration.ofMinutes(1));

//Create the Bucket and set the Bandwidth which we created above

Bucket bucket = Bucket.builder()

.addLimit(oneCosumePerMinuteLimit)

.build();

//Call method tryConsume to set count of Tokens to take from the Bucket,

//returns boolean, if true - consume successful and the Bucket had enough Tokens inside Bucket to execute method tryConsume

System.out.println(bucket.tryConsume(1)); //return true

//Call method tryConsumeAndReturnRemaining and set count of Tokens to take from the Bucket

//Returns ConsumptionProbe, which include much more information than tryConsume, such as the

//isConsumed - is method consume successful performed or not, if true - is successful

//getRemainingTokens - count of remaining Tokens

//getNanosToWaitForRefill - Time in nanoseconds to refill Tokens in our Bucket

ConsumptionProbe consumptionProbe = bucket.tryConsumeAndReturnRemaining(1);

System.out.println(consumptionProbe.isConsumed()); //return false since we have already called method tryConsume, but Bandwidth has a limit with rule - one token per one minute

System.out.println(consumptionProbe.getRemainingTokens()); //return 0, since we have already consumed all of the Tokens

System.out.println(consumptionProbe.getNanosToWaitForRefill()); //Return around 60000000000 nanoseconds

}

好的,我认为它看来最简单释义!

让我们考量一个更困难的值得注意。让我们想像一种情况,您须要考量通过对某个 RESTful API 方法有的乞求计至少来约束(须要通过来自某个用户对某个控制机的乞求codice_计至少来约束,每个 Y 间隔不多达 X 次)。【关注已为科学城,精彩学IT】但是,我们的的系统是分布式的,我们在一个战斗群中所有很多作者;我们适用 Hazelcast(但它可以是任何 JSR107 磁盘、DynamoDB、Redis 或其他东西)。

让我们基于 Spring 方法论来构建我们的示例。

首先,我们须要在 pom.xml 中所缓冲一些相反项:

com.github.vladimir-bukhtoyarov

bucket4j-hazelcast

7.0.0

javax.cache

cache-api

1.0.0

com.hazelcast

hazelcast

4.0.2

org.projectlombok

lombok

1.18.20

provided

对于下一步,我们不该考量在将来在控制机档次上适用释义:

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface RateLimiter {

TimeUnit timeUnit() default TimeUnit.MINUTES;

long timeValue();

long restriction();

}

此外,释义将分组 RateLimiter 释义(如果我们须要为每个控制机适用多个数据传输)。

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface RateLimiters {

RateLimiter[] value();

}

另外,须要缓冲重上新至少据类型:

public enum TimeUnit {

MINUTES, HOURS

}

而且,以前,我们须要创建者一个类,它将完成释义执行。由于将在控制机档次上新设释义,因此该类亦然 HandlerInterceptorAdapter 适配:

public class RateLimiterAnnotationHandlerInterceptorAdapter extends HandlerInterceptorAdapter {

//You should have already realized class, which returns Authentication context to getting userId

private AuthenticationUtil authenticationUtil;

private final ProxyManager proxyManager;

@Autowired

public RateLimiterAnnotationHandlerInterceptorAdapter(AuthenticationUtil authenticationUtil, HazelcastInstance hazelcastInstance) {

this.authenticationUtil = authenticationUtil;

//To start work with Hazelcast, you also should create HazelcastInstance bean

IMap bucketsMap = hazelcastInstance.getMap(HazelcastFrontConfiguration.RATE_LIMITER_BUCKET);

proxyManager = new HazelcastProxyManager<>(bucketsMap);

}

@Override

public boolean preHandle(HttpServletRequest request,

HttpServletResponse response,

Object handler) throws Exception {

if (handler instanceof HandlerMethod) {

HandlerMethod handlerMethod = (HandlerMethod) handler;

//if into handlerMethod is present RateLimiter or RateLimiters annotation, we get it, if not, we get empty Optional

Optional> rateLimiters = RateLimiterUtils.getRateLimiters(handlerMethod);

if (rateLimiters.isPresent()) {

//Get path from RequestMapping annotation(respectively we can get annotations such: GetMapping, PostMapping, PutMapping, DeleteMapping, because all of than annotations are extended from RequestMapping)

RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);

//To get unique key we use bundle of 2-x values: path from RequestMapping and user id

RateLimiterKey key = new RateLimiterKey(authenticationUtil.getPersonId(), requestMapping.value());

//Further we set key in proxy to get Bucket from cache or create a new Bucket

Bucket bucket = proxyManager.builder().build(key, () -> RateLimiterUtils.rateLimiterAnnotationsToBucketConfiguration(rateLimiters.get()));

//Try to consume token, if we don’t do that, we return 429 HTTP code

if (!bucket.tryConsume(1)) {

response.setStatus(429);

return false;

}

}

}

return true;

}

要适用 Hazelcast,我们须要创建者一个不必可序列化的可选基团:

@Data

@AllArgsConstructor

public class RateLimiterKey implements Serializable {

private String userId;

private String[] uri;

}

此外,不要忘记来由 RateLimiterUtils 的特殊而设计类,常用与 RateLimiterAnnotationHandlerInterceptorAdapter 四人社会活动(Spring 重上新名字誓约样式 - 将您的类或方法有命来由不必易于忽略,www.atguigu.com即使以您的实质上值得注意 10 个单词。这是我的期望格调)。

public final class RateLimiterUtils {

public static BucketConfiguration rateLimiterAnnotationsToBucketConfiguration(List rateLimiters) {

ConfigurationBuilder configBuilder = Bucket4j.configurationBuilder();

rateLimiters.stream().forEach(limiter -> configBuilder.addLimit(buildBandwidth(limiter)));

return configBuilder.build();

}

public static Optional> getRateLimiters(HandlerMethod handlerMethod) {

RateLimiters rateLimitersAnnotation = handlerMethod.getMethodAnnotation(RateLimiters.class);

if(rateLimitersAnnotation != null) {

return Optional.of(Arrays.asList(rateLimitersAnnotation.value()));

}

RateLimiter rateLimiterAnnotation = handlerMethod.getMethodAnnotation(RateLimiter.class);

if(rateLimiterAnnotation != null) {

return Optional.of(Arrays.asList(rateLimiterAnnotation));

}

return Optional.empty();

}

private static final Bandwidth buildBandwidth(RateLimiter rateLimiter) {

TimeUnit timeUnit = rateLimiter.timeUnit();

long timeValue = rateLimiter.timeValue();

long restriction = rateLimiter.restriction();

if (TimeUnit.MINUTES.equals(timeUnit)) {

return Bandwidth.simple(restriction, Duration.ofMinutes(timeValue));

} else if (TimeUnit.HOURS.equals(timeUnit)) {

return Bandwidth.simple(restriction, Duration.ofHours(timeValue));

} else {

return Bandwidth.simple(5000, Duration.ofHours(1));

}

}

}

还有一件有事; 我们须要在适配自 WebMvcConfigurerAdapter 的 Context 中所登记注册我们的可选拦截机:

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration

public class ContextConfig extends WebMvcConfigurerAdapter {

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new RateLimiterAnnotationHandlerInterceptorAdapter());

}

}

以前,为了测试我们的机制,我们将创建者 ExampleController 并在控制机的方法有右侧上新设 RateLimiter 以检查它是否正常社会活动:

import com.nibado.example.customargumentspring.component.RateLimiter;

import com.nibado.example.customargumentspring.component.RateLimiters;

import com.nibado.example.customargumentspring.component.TimeUnit;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class ExampleController {

@RateLimiters({@RateLimiter(timeUnit = TimeUnit.MINUTES, timeValue = 1, restriction = 2), @RateLimiter(timeUnit = TimeUnit.HOURS, timeValue = 1, restriction = 5)})

@GetMapping("/example/{id}")

public String example(@PathVariable("id") String id) {

return "ok";

}

}

在@RateLimiters 中所,我们上新设了两个约束:

@RateLimiter(timeUnit = TimeUnit.MINUTES, timeValue = 1,restriction = 2) — 每分钟不多达 2 个乞求。 @RateLimiter(timeUnit = TimeUnit.HOURS, timeValue = 1,restriction = 5) — 每不间断不多达 5 个乞求。

这只是 Bucket4j 奎的一小部分。如果你觉得这个奎很好的话,可以去研读更多API。

推荐选读:

Java技术开发技术之Javaweb具体内容jsjava中所json的适用

java技术开发juc并作之AQS入门

Java技术开发之模拟方法论Mockito

java技术开发方法论之JUnit 研读个人

苏州癫痫检查哪家医院好
北京白癜风医院哪家比较好
三亚男科医院哪家比较好
驻马店白癜风哪家医院最好
三亚看男科的医院哪家好

上一篇: 护理职业基础教育品牌智杰基础教育获希达资本Pre-A战略投资

下一篇: 荣耀“大玩家”发布,主打颜值和大读取,40W+256GB最低1399元

友情链接