package com.baiwang.customize.common.utils;
import java.time.LocalTime;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created in 2022/3/17
*/
public class limiterUtil {
/**
* 固定窗口算法
* 计算器
*/
static class RateLimiterSimpleWindow {
//阈值
private static Integer QPS = 3;
//时间窗口,毫秒级
private static long TIME_WINDOWS = 1000;
//计数器
private static AtomicInteger REQ_COUNT = new AtomicInteger();
private static long START_TIME = System.currentTimeMillis();
//每千毫秒限制请求QPS次
public synchronized static boolean tryAcquire() {
if ((System.currentTimeMillis() - START_TIME) > TIME_WINDOWS) {
REQ_COUNT.set(0);
START_TIME = System.currentTimeMillis();
}
return REQ_COUNT.incrementAndGet() <= QPS;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
LocalTime now = LocalTime.now();
if (!tryAcquire()) {
System.out.println(now + "被限流");
} else {
System.out.println(now + "做什么");
}
}
}
}
/**
* 滑动窗口算法
*/
static class RateLimiterSlidingWindow {
//阈值
private int qps = 2;
//时间窗口总大小毫秒级
private long windowSize = 1000;
//多少个子窗口
private Integer windowCount = 10;
//窗口列表
private windowInfo[] windowArray = new windowInfo[windowCount];
public RateLimiterSlidingWindow(int qps) {
this.qps = qps;
long currentTimeMills = System.currentTimeMillis();
for (int i = 0; i < windowArray.length; i++) {
windowArray[i] = new windowInfo(currentTimeMills, new AtomicInteger());
}
}
/**
* 1.计算当前时间创窗口
* 2.更新当前窗口计数 & 重置过期窗口计数
* 3.当前QPS是否会超过限制
*/
public synchronized boolean tryAcquire() {
long currentTimeMillis = System.currentTimeMillis();
//1.计算当前时间窗口
int currentIndex = (int) (currentTimeMillis % windowSize / (windowSize / windowCount));
//2.更新当前窗口计数 & 重置过期窗口计数
int sum = 0;
for (int i = 0; i < windowArray.length; i++) {
windowInfo windowInfo = windowArray[i];
if ((currentTimeMillis - windowInfo.getTime()) > windowSize) {
windowInfo.getNumber().set(0);
windowInfo.setTime(currentTimeMillis);
}
if (currentIndex == i && windowInfo.getNumber().get() < qps) {
windowInfo.getNumber().incrementAndGet();
}
sum = sum + windowInfo.getNumber().get();
}
return sum < qps;
}
private class windowInfo {
//窗口开始时间
private long time;
//计数器
private AtomicInteger number;
public windowInfo(long time, AtomicInteger number) {
this.number = number;
this.time = time;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public AtomicInteger getNumber() {
return number;
}
public void setNumber(AtomicInteger number) {
this.number = number;
}
}
}
public static void main(String[] args) throws InterruptedException {
int qps = 2, count = 20, sleep = 300, success = count * sleep / 1000 * qps;
System.out.println(String.format("当前QPS限制为:%d,当前测试次数:%d,间隔:%dms,预计成功次数:%d", qps, count, sleep, success));
success = 0;
RateLimiterSlidingWindow rateLimiterSlidingWindow = new RateLimiterSlidingWindow(qps);
for (int i = 0; i < count; i++) {
Thread.sleep(sleep);
if (rateLimiterSlidingWindow.tryAcquire()) {
success++;
if (success % qps == 0) {
System.out.println(LocalTime.now() + ":success, ");
} else {
System.out.println(LocalTime.now() + ":success, ");
}
} else {
System.out.println(LocalTime.now() + ":fail, ");
}
}
System.out.println();
System.out.println("实际测试成功次数:" + success);
}
}
上面是固定窗口算法和滑动窗口算法的实现思路。