package com.dobbinsoft.fw.support.rate;
|
|
import com.dobbinsoft.fw.core.annotation.HttpMethod;
|
import com.dobbinsoft.fw.core.annotation.RateLimitType;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
import java.util.concurrent.TimeUnit;
|
|
/**
|
* ClassName: RateLimiterRedisCount
|
* Description: Redis实现计数器 限流器
|
*
|
* @author: e-weichaozheng
|
* @date: 2021-04-12
|
*/
|
public class RateLimiterRedisCount implements RateLimiter {
|
|
@Autowired
|
private StringRedisTemplate lockRedisTemplate;
|
|
private static final String COUNTER_BUCKET = "RT_COUNTER_";
|
|
@Override
|
public boolean acquire(String fullMethod, HttpMethod httpMethod, Long personId, String ip) {
|
if (httpMethod.rateLimit() != RateLimitType.NONE) {
|
String key = COUNTER_BUCKET + fullMethod;
|
if (httpMethod.rateLimit() == RateLimitType.USER_ID) {
|
key = key + "_U_" + personId;
|
} else if (httpMethod.rateLimit() == RateLimitType.IP) {
|
key = key + "_P_" + ip;
|
}
|
key = key + "_S_" + httpMethod.rateWindow();
|
|
lockRedisTemplate.opsForValue().setIfAbsent(key, "0", httpMethod.rateWindow(), TimeUnit.SECONDS);
|
Long increment = lockRedisTemplate.opsForValue().increment(key, 1l);
|
// 计数器清0:当键值过期时,计数器清0
|
// 计数器限流,存在临界问题
|
// eg. 一个资源若允许60秒访问1000次。用户可以在00:59这一秒请求1000次,在01:00这一秒请求1000次,会导致在2秒内访问2000次,远超60秒1000次的设计。
|
if (increment > httpMethod.rate()) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
}
|