限流器的算法选项
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
Java是一种非常流行的编程语言,具有广泛的应用场景。在Java中,实现限流器的方法有很多种。本文将介绍一些常见的实现方法和技术。
令牌桶算法
令牌桶算法是一种常见的限流算法,它基于一个令牌桶来控制请求的速率。在令牌桶算法中,令牌桶以固定的速率生成令牌,并将这些令牌存储在桶中。每当一个请求到达时,它必须从桶中获取一个令牌才能被处理。如果桶中没有足够的令牌,请求将被拒绝。
在Java中,可以使用Guava库中的RateLimiter类来实现令牌桶算法。RateLimiter类提供了一种简单而有效的方式来控制请求的速率。
接下来我们开发带着大家手把手去实现一个属于我们自己的限流器服务组件。
开发阶段
Maven的依赖配置
首先,我们需要开始配置Maven所具有的依赖关系,主要包含者基础的工具类配置信息包含了Hutools和apache-commons相关的
基础工具组件,以及针对于我们的限流器的服务库Guava。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fengwenyi</groupId>
<artifactId>JavaLib</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--joda-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.8</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
核心服务类之间的关系
主要包含了各个服务类之间的关系,包含了继承关系以及服务功能的继承总览机制。
定义限流器基础接口
首先,我们先定义一下我们的基础接口-ExecuteRateLimiter。
@FunctionalInterface
public interface ExecuteRateLimiter<P,R>{
/**
* 执行操作
* @param param
* @return
*/
R execute(P param);
}
这个各类主要代表限流器的执行操作方法。
定义限流器工厂方法
接下来,我们定义一下限流器工厂方法类ExecuteRateLimiterFactory。主要目的作为我们限流器的创建限流器的功能。
@FunctionalInterface
public interface ExecuteRateLimiterFactory<P,R> {
R create(P param);
}
主要目的作为创建限流器ExecuteRateLimiter接口,接下来我们会进行覆盖和实现改接口进行构建不同厂商的限流器服务实现类。
Guava厂商的限流器所需要的参数类 — GuavaRateLimiterParam
在此,我们主要去实现我们 的限流器的参数实现类:GuavaRateLimiterParam。
@Builder
@Data
public class GuavaRateLimiterParam {
private int permitsPerSecond;
private int warmupPeriod;
private TimeUnit timeUnit;
}
主要去属于封装了对应的Guava限流器的参数属性:
- permitsPerSecond: 返回的RateLimiter的速率,意味着每秒有多少个许可变成有效。
- warmupPeriod: 在这段时间内RateLimiter会增加它的速率,在抵达它的稳定速率或者最大速率之前
- timeUnit:参数warmupPeriod 的时间单位
Guava厂商的限流器工厂类 — GuavaExecuteRateLimiterFactory
首先,我们需要进行实现属于我们Guava厂商的限流器的工厂类,主要目的是去实现对应的Guava的限流器的核心类:RateLimiter,并且作为我们Spring容器的一个组件进行注册到容器中去。
@Component
public class GuavaExecuteRateLimiterFactory implements ExecuteRateLimiterFactory<GuavaRateLimiterParam,RateLimiter> {
/**
* 创建RateLimiter对象
* @param param
* @return
*/
@Override
public RateLimiter create(GuavaRateLimiterParam param) {
return RateLimiter.create(param.getPermitsPerSecond(),param.getWarmupPeriod(),param.getTimeUnit());
}
}
实现面向切面模式的限流器实现功能
- 限流器注解
- 限流器切面
限流器注解
限流器的注解,主要面向于限流器的功能的参数包装,方便开发者可以再注解上进行定义不同的参数选项,基本等价于我们的GuavaRateLimiterParam的属性参数。可见:
@java.lang.annotation.Target({ElementType.METHOD,ElementType.FIELD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@Component
@Autowired(required = false)
public @interface GuavaExecuteRateLimiter {
/**
* 返回的RateLimiter的速率,意味着每秒有多少个许可变成有效。
*/
int permitsPerSecond() default 500;
/**
* 在这段时间内RateLimiter会增加它的速率,在抵达它的稳定速率或者最大速率之前
*/
int warmupPeriod() default 5;
/**
* 参数warmupPeriod 的时间单位
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
限流器切面
主要针对于限流器的切面类进行控制处理-GuavaExecuteRateLimterAspect类。
@Slf4j
@Aspect
@Component
public class GuavaExecuteRateLimterAspect {
@Pointcut("@annotation(com.hyts.assemble.ratelimiter.guava.anno.GuavaExecuteRateLimiter)")
public void methodPointCut() {}
@Autowired
GuavaExecuteRateLimiterFactory executeRateLimiterFactory;
ConcurrentHashMap<String, RateLimiter> rateLimiterConcurrentHashMap = new ConcurrentHashMap<>();
Joiner joiner = Joiner.on("-").skipNulls();
@Around("methodPointCut()")
public Object doMethod(ProceedingJoinPoint proceedingJoinPoint){
MethodSignature methodSignature = (MethodSignature)proceedingJoinPoint.getSignature();
Method method = methodSignature.getMethod();
GuavaExecuteRateLimiter guavaExecuteRateLimiter = method.getAnnotation(GuavaExecuteRateLimiter.class);
GuavaRateLimiterParam guavaRateLimiterParam = GuavaRateLimiterParam.builder().
permitsPerSecond(guavaExecuteRateLimiter.permitsPerSecond()).
timeUnit(guavaExecuteRateLimiter.timeUnit()).
warmupPeriod(guavaExecuteRateLimiter.warmupPeriod()).build();
String key = joiner.join(guavaExecuteRateLimiter.permitsPerSecond(),
guavaExecuteRateLimiter.timeUnit().toString()
,guavaExecuteRateLimiter.warmupPeriod());
RateLimiter rateLimiter = rateLimiterConcurrentHashMap.
computeIfAbsent(key,param-> executeRateLimiterFactory.create(guavaRateLimiterParam));
try {
double rateValue = rateLimiter.acquire();
log.info("执行限流方法操作处理:当前qps:{} delay rate limiter value:{}",guavaExecuteRateLimiter.permitsPerSecond(),rateValue);
return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
} catch (Throwable e) {
log.error("执行限流控制方法失败!",e);
return null;
}
}
}
主要用于通过AOP进行实时构建GuavaExecuteRateLimiterFactory进行构建和创建对应的RateLimiter对象,并且缓存到对应的容器里面进行构建。
进行执行限流操纵控制。