需求是每1000毫秒内,只允许同一个ip访问2次
效果如图:
代码如下:
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
class RequestRecord {
private final Instant timestamp;
private final AtomicInteger count = new AtomicInteger(1);
public RequestRecord(Instant timestamp) {
this.timestamp = timestamp;
}
public Instant getTimestamp() {
return timestamp;
}
public int getCount() {
return count.get();
}
public void increment() {
count.incrementAndGet();
}
}
class SlidingWindowRateLimiter {
private final ConcurrentSkipListMap<Instant, RequestRecord> records;
private final long windowSizeInMillis;
private final int limit;
public SlidingWindowRateLimiter(long windowSizeInMillis, int limit) {
this.records = new ConcurrentSkipListMap<>();
this.windowSizeInMillis = windowSizeInMillis;
this.limit = limit;
}
public synchronized boolean allowRequest() {
Instant now = Instant.now();
// 移除窗口外的旧记录
records.headMap(now.minusMillis(windowSizeInMillis)).clear();
// 检查当前窗口内是否已达到请求限制
int totalRequests = records.values().stream().mapToInt(RequestRecord::getCount).sum();
if (totalRequests >= limit) {
return false;
}
// 添加当前请求记录
records.compute(now, (key, value) -> {
if (value == null) {
return new RequestRecord(now);
} else {
value.increment();
return value;
}
});
return true;
}
}
public class IPBasedSlidingWindowRateLimiter {
private final ConcurrentMap<String, SlidingWindowRateLimiter> ipRateLimiters;
private final long windowSizeInMillis;
private final int limitPerWindow;
public IPBasedSlidingWindowRateLimiter(long windowSizeInMillis, int limitPerWindow) {
this.ipRateLimiters = new ConcurrentHashMap<>();
this.windowSizeInMillis = windowSizeInMillis;
this.limitPerWindow = limitPerWindow;
}
public synchronized boolean allowRequest(String ipAddress) {
SlidingWindowRateLimiter limiter = ipRateLimiters.computeIfAbsent(ipAddress,
k -> new SlidingWindowRateLimiter(windowSizeInMillis, limitPerWindow));
return limiter.allowRequest();
}
public static String curDateTimeStr() {
Calendar calendar2 = Calendar.getInstance();
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf2.format(calendar2.getTime());
}
public static void main(String[] args) throws InterruptedException {
// http请求根据ip限制频率
IPBasedSlidingWindowRateLimiter rateLimiter = new IPBasedSlidingWindowRateLimiter(1000, 2);
String ip = "1.1.1.1";
String ip2 = "2.2.2.2";
for (int i = 0; i < 200; i++) {
if (rateLimiter.allowRequest(ip)) {
System.out.println("["+curDateTimeStr()+"] "+i+":允许访问"+ip);
} else {
System.out.println("["+curDateTimeStr()+"] "+i+":访问受限"+ip + "," + (rateLimiter.allowRequest(ip2) ? "允许访问"+ip2 : "访问受限"+ip2));
Thread.sleep(2000L);
}
}
}
}