之前使用了 JDK 自带的 Map
实现了自定义限速的简单需求。在当时的实现当中,有一个被隐藏的小设计,就是如果是用使用异步线程,用来根据配置给请求次数数据重置。如此这样,校验方法会非常简单方便。
对于普通 Java
项目来说,如果使用异步线程处理,除了 deamon
进程以外,其他实现的确有点麻烦。即使 deamon
线程也很难做到完全的实用性,所以才使用了上篇文章的实现方案。
之前提到过一个非常有趣的高性能本地缓存 Caffeine
刚好能解决这个问题,可以通过缓存过期或者定时刷新功能来实现定时刷新的需求。这里我选择了定时刷新功能,这种选择会限制限流配置的种类,无法进行 2/3s
, 10/2s
配置,我最终选择 TPS
进行配置,全部使用 1s
为限制周期。
主要思路如下:
代码如下:
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
/**
* 限流工具,基于Caffeine实现,支持动态配置,根据TPS限流
*/
class TpsLimit {
Map<String, Integer> qpsConfig = [:]
LoadingCache<Object, AtomicInteger> build = Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.SECONDS).build((key) -> {
return new AtomicInteger()
})
/**
* 是否限流
* @param key
* @return
*/
boolean isLimit(String key) {
AtomicInteger atomicInteger = build.get(key)
if (atomicInteger.get() >= qpsConfig.get(key, 1)) {
return true
}
atomicInteger.incrementAndGet()
return false
}
/**
* 添加限流配置
* @param key
* @param qps
* @return
*/
def addConfig(String key, Integer qps) {
qpsConfig.put(key, qps)
}
}
测试脚本如下,与前一篇文章大同小异:
import com.funtester.httpclient.FunHttp
import com.funtester.utils.TpsLimit
class Routine extends FunHttp {
static void main(String[] args) {
def limit = new TpsLimit()
limit.addConfig("test", 1)
1000.times {
sleep(0.1)
fun {
def limit1 = limit.isLimit("t4est")
if (!limit1) {
output("未限流")
}
}
} }
}
控制台输出:
22:19:20:545 F-1 未限流
22:19:20:644 F-2 未限流
22:19:22:094 F-8 未限流
22:19:22:195 F-1 未限流
22:19:24:048 F-3 未限流
22:19:24:150 F-4 未限流
22:19:25 uptime:6 s
22:19:25 finished: 49 task
可以看出,按照默认配置 1 TPS 的配置实现。