本地限流
一、技术选型
- 使用Google的guava缓存提供的工具RateLimiter进行限流
- 基本概念:
RateLimiter:使用的是令牌桶算法,有一个固定容量的桶,系统会以固定的速率向桶中添加令牌(token)。每个请求需要从桶中获取一个令牌才能被执行。如果桶中没有足够的令牌,请求就需要等待直到有足够的令牌可用,或者直接被拒绝。 - 主要方法
(1)创建实例
RateLimiter.create(double permitsPerSecond)创建一个实例,permitsPerSecond 参数指定了每秒允许的最大请求数(即生成令牌的速率)。
(2)方法使用
acquire():尝试获取一个许可,并阻塞当前线程直到有足够的令牌可用。该方法返回实际等待的时间(秒)。
acquire(int permits):尝试获取指定数量的许可,并阻塞当前线程直到有足够的令牌可用。
tryAcquire():非阻塞版本,尝试立即获取一个许可,如果无法立即获取,则返回 false。
tryAcquire(int permits, long timeout, TimeUnit unit):尝试在指定时间内获取指定数量的许可,如果成功则返回 true,否则返回 false。
二、代码示例
- maven引用
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
- Java代码实现
- 创建一个枚举“LocalLimit”,表示需要限流的类型
package com.zzc.common.utils;
public enum LocalLimit {
TEST("TEST", 2),
;
private final String type;
private final double limit;
LocalLimit(String type, long limit) {
this.type = type;
this.limit = limit;
}
public String getType() {
return type;
}
public double getLimit() {
return limit;
}
}
- 创建具体实现限流的工具类“LocalRateLimit”
package com.zzc.common.utils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class LocalRateLimit {
private static final Cache<String, RateLimiter> RATE_LIMITER_CACHE = CacheBuilder.newBuilder()
.expireAfterWrite(9, TimeUnit.HOURS)
.initialCapacity(1000)
.maximumSize(20000)
.build();
public static boolean tryAcquire(LocalLimit type) {
return tryAcquire(type, "");
}
public static boolean tryAcquire(LocalLimit type, String subKey) {
RateLimiter rateLimiter = getRateLimiter(type, subKey);
if (rateLimiter != null) {
return rateLimiter.tryAcquire();
}
return false;
}
private static RateLimiter getRateLimiter(LocalLimit type, String subKey) {
try {
RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(getFinalKey(type, subKey), () -> RateLimiter.create(type.getLimit()));
return rateLimiter;
} catch (Exception e) {
}
return null;
}
private static String getFinalKey(LocalLimit key, String subKey) {
return key + ":" + subKey;
}
}