mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-30 10:47:33 +02:00
Reduce memory consumption of anti bot feature
This commit is contained in:
@ -27,7 +27,9 @@ package com.github.games647.fastlogin.core;
|
|||||||
|
|
||||||
import com.google.common.base.Ticker;
|
import com.google.common.base.Ticker;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
|
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
|
||||||
@ -37,20 +39,22 @@ public class TickingRateLimiter implements RateLimiter {
|
|||||||
|
|
||||||
private final Ticker ticker;
|
private final Ticker ticker;
|
||||||
|
|
||||||
private final long[] requests;
|
// amount of milliseconds to expire
|
||||||
private final long expireTime;
|
private final long expireTime;
|
||||||
private int position;
|
|
||||||
|
// total request limit
|
||||||
|
private final int requestLimit;
|
||||||
|
|
||||||
|
private final Deque<TimeRecord> records;
|
||||||
|
private int totalRequests;
|
||||||
|
|
||||||
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
|
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
|
||||||
this.ticker = ticker;
|
this.ticker = ticker;
|
||||||
|
|
||||||
this.requests = new long[maxLimit];
|
this.requestLimit = maxLimit;
|
||||||
this.expireTime = expireTime;
|
this.expireTime = expireTime;
|
||||||
|
|
||||||
// fill the array with expired entry, because nanoTime could overflow and include negative numbers
|
records = new ArrayDeque<>(10);
|
||||||
long nowMilli = ticker.read() / 1_000_000;
|
|
||||||
long initialVal = nowMilli - expireTime;
|
|
||||||
Arrays.fill(requests, initialVal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,14 +68,80 @@ public class TickingRateLimiter implements RateLimiter {
|
|||||||
long nowMilli = ticker.read() / 1_000_000;
|
long nowMilli = ticker.read() / 1_000_000;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
// having synchronized will limit the amount of concurrency a lot
|
// having synchronized will limit the amount of concurrency a lot
|
||||||
long oldest = requests[position];
|
TimeRecord oldest = records.peekFirst();
|
||||||
if (nowMilli - oldest >= expireTime) {
|
if (oldest != null && oldest.hasExpired(nowMilli)) {
|
||||||
requests[position] = nowMilli;
|
records.pop();
|
||||||
position = (position + 1) % requests.length;
|
totalRequests -= oldest.getRequestCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// total requests reached block any further requests
|
||||||
|
if (totalRequests >= requestLimit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeRecord latest = records.peekLast();
|
||||||
|
if (latest == null) {
|
||||||
|
// empty list - add new record
|
||||||
|
records.add(new TimeRecord(nowMilli, expireTime));
|
||||||
|
totalRequests++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
int res = latest.compareTo(nowMilli);
|
||||||
|
if (res < 0) {
|
||||||
|
// now is before than the record means time jumps
|
||||||
|
throw new IllegalStateException("Time jumped back");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
// same minute record
|
||||||
|
latest.hit();
|
||||||
|
totalRequests++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now is one minute newer
|
||||||
|
records.add(new TimeRecord(nowMilli, expireTime));
|
||||||
|
totalRequests++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TimeRecord implements Comparable<Long> {
|
||||||
|
|
||||||
|
private final long firstMinuteRecord;
|
||||||
|
private final long expireTime;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public TimeRecord(long firstMinuteRecord, long expireTime) {
|
||||||
|
this.firstMinuteRecord = firstMinuteRecord;
|
||||||
|
this.expireTime = expireTime;
|
||||||
|
this.count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hit() {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRequestCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasExpired(long now) {
|
||||||
|
return firstMinuteRecord + expireTime <= now;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Long other) {
|
||||||
|
if (other < firstMinuteRecord) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other > firstMinuteRecord + TimeUnit.MINUTES.toMillis(1)) {
|
||||||
|
return +1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ import org.junit.Test;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class RateLimiterTest {
|
public class TickingRateLimiterTest {
|
||||||
|
|
||||||
private static final long THRESHOLD_MILLI = 10;
|
private static final long THRESHOLD_MILLI = 10;
|
||||||
|
|
Reference in New Issue
Block a user