CacheSource增加rateLimit接口
This commit is contained in:
@@ -66,7 +66,8 @@ public abstract class AbstractCacheSource extends AbstractService implements Cac
|
|||||||
}
|
}
|
||||||
|
|
||||||
//根据配置中创建DataSource
|
//根据配置中创建DataSource
|
||||||
public static CacheSource createCacheSource(ClassLoader serverClassLoader, ResourceFactory resourceFactory, AnyValue sourceConf, String sourceName, boolean compileMode) throws Exception {
|
public static CacheSource createCacheSource(ClassLoader serverClassLoader, ResourceFactory resourceFactory,
|
||||||
|
AnyValue sourceConf, String sourceName, boolean compileMode) throws Exception {
|
||||||
CacheSource source = null;
|
CacheSource source = null;
|
||||||
if (serverClassLoader == null) {
|
if (serverClassLoader == null) {
|
||||||
serverClassLoader = Thread.currentThread().getContextClassLoader();
|
serverClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ public final class CacheMemorySource extends AbstractCacheSource {
|
|||||||
|
|
||||||
private final ReentrantLock containerLock = new ReentrantLock();
|
private final ReentrantLock containerLock = new ReentrantLock();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, RateLimitEntry> rateLimitContainer = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final ReentrantLock rateLimitContainerLock = new ReentrantLock();
|
||||||
|
|
||||||
//key: topic
|
//key: topic
|
||||||
private final Map<String, Set<CacheEventListener<byte[]>>> pubsubListeners = new ConcurrentHashMap<>();
|
private final Map<String, Set<CacheEventListener<byte[]>>> pubsubListeners = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -736,6 +740,75 @@ public final class CacheMemorySource extends AbstractCacheSource {
|
|||||||
return supplyFuture(() -> delex(key, expectedValue));
|
return supplyFuture(() -> delex(key, expectedValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌桶算法限流, 返回负数表示无令牌, 其他为有令牌
|
||||||
|
* <pre>
|
||||||
|
* 每秒限制请求1次: rate:1, capacity:1, requested:1
|
||||||
|
* 每秒限制请求10次: rate:10, capacity:10, requested:1
|
||||||
|
* 每分钟限制请求1次: rate:1, capacity:60, requested:60
|
||||||
|
* 每分钟限制请求10次: rate:1, capacity:60, requested:6
|
||||||
|
* 每小时限制请求1次: rate:1, capacity:3600, requested:3600
|
||||||
|
* 每小时限制请求10次: rate:1, capacity:3600, requested:360
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param key 限流的键
|
||||||
|
* @param rate 令牌桶每秒填充平均速率
|
||||||
|
* @param capacity 令牌桶总容量
|
||||||
|
* @param requested 需要的令牌数
|
||||||
|
*
|
||||||
|
* @return 可用令牌数
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long rateLimit(final String key, final long rate, final long capacity, final long requested) {
|
||||||
|
if (key == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
if (capacity < rate || capacity < requested || rate <= 0 || requested < 0) {
|
||||||
|
throw new IllegalArgumentException("rate=" + rate + ", capacity=" + capacity + ", requested=" + requested);
|
||||||
|
}
|
||||||
|
RateLimitEntry entry = null;
|
||||||
|
rateLimitContainerLock.lock();
|
||||||
|
try {
|
||||||
|
entry = rateLimitContainer.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
long newTokens = capacity - requested;
|
||||||
|
entry = new RateLimitEntry(key, newTokens);
|
||||||
|
rateLimitContainer.put(key, entry);
|
||||||
|
return newTokens;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
rateLimitContainerLock.unlock();
|
||||||
|
}
|
||||||
|
entry.lock();
|
||||||
|
try {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long ttl = capacity / rate * 2 * 1000;
|
||||||
|
if (entry.isExpired()) {
|
||||||
|
entry.milliSeconds(ttl).tokens = capacity;
|
||||||
|
}
|
||||||
|
long delta = Math.max(0, (now - entry.timestamp) / 1000);
|
||||||
|
long filledTokens = Math.min(capacity, entry.tokens + (delta * rate));
|
||||||
|
boolean allowed = filledTokens >= requested;
|
||||||
|
long newTokens = filledTokens;
|
||||||
|
if (allowed) {
|
||||||
|
newTokens = filledTokens - requested;
|
||||||
|
}
|
||||||
|
if (ttl > 0) {
|
||||||
|
entry.milliSeconds(ttl);
|
||||||
|
entry.tokens = newTokens;
|
||||||
|
entry.timestamp = now;
|
||||||
|
}
|
||||||
|
return allowed ? newTokens : (filledTokens - requested);
|
||||||
|
} finally {
|
||||||
|
entry.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Long> rateLimitAsync(String key, long rate, long capacity, long requested) {
|
||||||
|
return supplyFuture(() -> rateLimit(key, rate, capacity, requested));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long incr(final String key) {
|
public long incr(final String key) {
|
||||||
return incrby(key, 1);
|
return incrby(key, 1);
|
||||||
@@ -2433,6 +2506,70 @@ public final class CacheMemorySource extends AbstractCacheSource {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class RateLimitEntry {
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
volatile long lastAccessed; //最后刷新时间
|
||||||
|
|
||||||
|
//<=0表示永久保存
|
||||||
|
private long expireMills;
|
||||||
|
|
||||||
|
private long initTime;
|
||||||
|
|
||||||
|
//令牌数
|
||||||
|
private long tokens;
|
||||||
|
|
||||||
|
//时间戳,单位:毫秒
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public RateLimitEntry(String key, long tokens) {
|
||||||
|
this.key = key;
|
||||||
|
this.tokens = tokens;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateLimitEntry milliSeconds(long milliSeconds) {
|
||||||
|
this.initTime = System.currentTimeMillis();
|
||||||
|
this.expireMills = milliSeconds > 0 ? milliSeconds : 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JsonFactory.root().getConvert().convertTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConvertColumn(ignore = true)
|
||||||
|
public boolean isExpired() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
return expireMills > 0 && (initTime + expireMills) < now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lock() {
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlock() {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpireMills() {
|
||||||
|
return expireMills;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastAccessed() {
|
||||||
|
return lastAccessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static enum CacheEntryType {
|
public static enum CacheEntryType {
|
||||||
OBJECT, ATOMIC, DOUBLE, SSET, ZSET, LIST, MAP;
|
OBJECT, ATOMIC, DOUBLE, SSET, ZSET, LIST, MAP;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -363,7 +363,29 @@ public interface CacheSource extends Resourcable {
|
|||||||
return getDel(key, Long.class);
|
return getDel(key, Long.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------ 键 Keys ------------------------
|
//------------------------ 键 Keys ------------------------
|
||||||
|
/**
|
||||||
|
* 令牌桶算法限流, 返回负数表示无令牌, 其他为有令牌
|
||||||
|
* <pre>
|
||||||
|
* 每秒限制请求1次: rate:1, capacity:1, requested:1
|
||||||
|
* 每秒限制请求10次: rate:10, capacity:10, requested:1
|
||||||
|
* 每分钟限制请求1次: rate:1, capacity:60, requested:60
|
||||||
|
* 每分钟限制请求10次: rate:1, capacity:60, requested:6
|
||||||
|
* 每小时限制请求1次: rate:1, capacity:3600, requested:3600
|
||||||
|
* 每小时限制请求10次: rate:1, capacity:3600, requested:360
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param key 限流的键
|
||||||
|
* @param rate 令牌桶每秒填充平均速率
|
||||||
|
* @param capacity 令牌桶总容量
|
||||||
|
* @param requested 需要的令牌数
|
||||||
|
*
|
||||||
|
* @return 可用令牌数
|
||||||
|
*/
|
||||||
|
default long rateLimit(String key, long rate, long capacity, long requested) {
|
||||||
|
return rateLimitAsync(key, rate, capacity, requested).join();
|
||||||
|
}
|
||||||
|
|
||||||
default long del(String... keys) {
|
default long del(String... keys) {
|
||||||
return delAsync(keys).join();
|
return delAsync(keys).join();
|
||||||
}
|
}
|
||||||
@@ -1304,6 +1326,26 @@ public interface CacheSource extends Resourcable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------ 键 Keys ------------------------
|
//------------------------ 键 Keys ------------------------
|
||||||
|
/**
|
||||||
|
* 令牌桶算法限流, 返回负数表示无令牌, 其他为有令牌
|
||||||
|
* <pre>
|
||||||
|
* 每秒限制请求1次: rate:1, capacity:1, requested:1
|
||||||
|
* 每秒限制请求10次: rate:10, capacity:10, requested:1
|
||||||
|
* 每分钟限制请求1次: rate:1, capacity:60, requested:60
|
||||||
|
* 每分钟限制请求10次: rate:1, capacity:60, requested:6
|
||||||
|
* 每小时限制请求1次: rate:1, capacity:3600, requested:3600
|
||||||
|
* 每小时限制请求10次: rate:1, capacity:3600, requested:360
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param key 限流的键
|
||||||
|
* @param rate 令牌桶每秒填充平均速率
|
||||||
|
* @param capacity 令牌桶总容量
|
||||||
|
* @param requested 需要的令牌数
|
||||||
|
*
|
||||||
|
* @return 可用令牌数
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Long> rateLimitAsync(String key, long rate, long capacity, long requested);
|
||||||
|
|
||||||
public CompletableFuture<Long> delexAsync(String key, String expectedValue);
|
public CompletableFuture<Long> delexAsync(String key, String expectedValue);
|
||||||
|
|
||||||
public CompletableFuture<Long> delAsync(String... keys);
|
public CompletableFuture<Long> delAsync(String... keys);
|
||||||
|
|||||||
Reference in New Issue
Block a user