固定窗口计数器算法Java实现
Java Implementation of Fixed Window Counter Algorithm
实现
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
/**
* 固定窗口计数器,使用固定窗口计数器算法进行限流
*
* @author qzy
*
*/
public class FixedWindowCounter {
private static final ZoneId SYSTEM_DEFAULT_ZONE = ZoneId.systemDefault();// 系统默认时区
private double limit;// 单个窗口内的配额限制
private long windowDuration;// 窗口大小
private ChronoUnit windowDurationUnit;// 窗口大小的单位(ms、s、m、h)
private double count;// 当前窗口内的已用配额
private long nextWindowStart;// 下一个窗口的起始时间戳
/**
* @param limit 单个窗口内的配额限制
* @param windowDuration 窗口大小
* @param windowDurationUnit 窗口大小的单位(ms、s、m、h)
*/
public FixedWindowCounter(double limit, long windowDuration, ChronoUnit windowDurationUnit) {
this.limit = limit;
this.windowDuration = windowDuration;
this.windowDurationUnit = windowDurationUnit;
}
/**
* 请求配额
*
* @param number 所需配额
* @return true表示请求成功,false表示请求失败
*/
public synchronized boolean acquire(double number) {
long now = System.currentTimeMillis();
if (now >= nextWindowStart) {// 进入了下一个窗口
count = 0;// 将计数器归零
nextWindowStart = calculateNextWindowStart(now);// 计算下一个窗口的起始时间戳
}
if (number > limit - count) {// 配额不够用
return false;
}
count += number;// 记录已用配额
return true;
}
/**
* 计算下一个窗口的起始时间戳
*
* @param now
* @return
*/
private long calculateNextWindowStart(long now) {
ChronoUnit truncatedToUnit;
switch (windowDurationUnit) {
case MILLIS:
truncatedToUnit = ChronoUnit.SECONDS;
break;
case SECONDS:
truncatedToUnit = ChronoUnit.MINUTES;
break;
case MINUTES:
truncatedToUnit = ChronoUnit.HOURS;
break;
case HOURS:
truncatedToUnit = ChronoUnit.DAYS;
break;
default:
throw new RuntimeException("不支持的时间窗口大小");
}
Instant nowInstant = Instant.ofEpochMilli(now);
Instant start = nowInstant.atZone(SYSTEM_DEFAULT_ZONE).truncatedTo(truncatedToUnit).toInstant();
while (!start.isAfter(nowInstant)) {// 如果窗口起始时间不在当前时间之后
start = start.plus(windowDuration, windowDurationUnit);// 向后移动一个窗口
}
return start.toEpochMilli();
}
}
测试
import java.time.temporal.ChronoUnit;
public class Test {
public static void main(String[] args) {
FixedWindowCounter fixedWindowCounter = new FixedWindowCounter(7, 10, ChronoUnit.SECONDS);
long totalCount = 0, successCount = 0;
while (true) {
totalCount++;
if (fixedWindowCounter.acquire(1)) {
successCount++;
System.out.printf("%s\t%s\t%s\n", System.currentTimeMillis(), successCount, totalCount);
}
}
}
}