parent
bc2f1bbc9b
commit
5d3e2581b1
@ -1,47 +0,0 @@ |
||||
package cn.toesbieya.jxc.interceptor; |
||||
|
||||
import cn.toesbieya.jxc.model.vo.Result; |
||||
import cn.toesbieya.jxc.model.vo.UserVo; |
||||
import cn.toesbieya.jxc.module.request.RequestModule; |
||||
import cn.toesbieya.jxc.util.*; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
@Slf4j |
||||
public class RateControlInterceptor implements HandlerInterceptor { |
||||
@Override |
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||
UserVo user = SessionUtil.get(request); |
||||
|
||||
if (user.isAdmin()) return true; |
||||
|
||||
String url = request.getServletPath(); |
||||
String ip = IpUtil.getIp(request); |
||||
|
||||
int result = RequestModule.pass(url, ip); |
||||
|
||||
switch (result) { |
||||
case -1: |
||||
log.warn("限流拦截,服务的总请求频率超出,url:{},用户:{},ip:{}", url, user.getName(), ip); |
||||
WebUtil.responseJson(response, Result.overload()); |
||||
break; |
||||
case 0: |
||||
log.warn("限流拦截,ip已被暂时拉黑,url:{},用户:{},ip:{}", url, user.getName(), ip); |
||||
WebUtil.responseJson(response, Result.tooManyRequest()); |
||||
break; |
||||
case -2: |
||||
log.warn("限流拦截,ip的请求频率超出,url:{},用户:{},ip:{}", url, user.getName(), ip); |
||||
WebUtil.responseJson(response, Result.tooManyRequest()); |
||||
break; |
||||
case 1: |
||||
return true; |
||||
} |
||||
|
||||
ThreadUtil.clearUser(); |
||||
|
||||
return false; |
||||
} |
||||
} |
||||
@ -1,15 +0,0 @@ |
||||
package cn.toesbieya.jxc.module.request; |
||||
|
||||
import com.google.common.util.concurrent.RateLimiter; |
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class RateLimiterHelper { |
||||
private RateLimiter limiter; |
||||
private long ipRate; |
||||
|
||||
public RateLimiterHelper(long totalRate, long ipRate) { |
||||
this.limiter = RateLimiter.create(totalRate); |
||||
this.ipRate = ipRate; |
||||
} |
||||
} |
||||
@ -1,86 +0,0 @@ |
||||
package cn.toesbieya.jxc.module.request; |
||||
|
||||
import cn.toesbieya.jxc.model.vo.ResourceVo; |
||||
import cn.toesbieya.jxc.service.sys.SysResourceService; |
||||
import cn.toesbieya.jxc.util.RedisUtil; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.List; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class RequestModule { |
||||
private final static String IP_LIMIT_KEY = "limit:url-ip:"; |
||||
private final static String BLACK_IP_KEY = "limit:black-ip:"; |
||||
//判断能拿到令牌的最大时间,毫秒
|
||||
private final static long TIME_OUT = 200L; |
||||
//ip暂时拉黑的时间,单位秒
|
||||
private final static long EXPIRE_SECONDS = 10L; |
||||
//url的总访问频率限制
|
||||
private static ConcurrentHashMap<String, RateLimiterHelper> limitMap; |
||||
private SysResourceService service; |
||||
|
||||
/** |
||||
* 未在resource表中配置的不进行限制 |
||||
* |
||||
* @param url 请求的url |
||||
* @param ip 访问IP |
||||
* @return 0:ip被暂时拉黑,-1:该url的服务暂时不可用,-2:该ip访问该url时超出规定速度,1:通过 |
||||
*/ |
||||
public static int pass(String url, String ip) { |
||||
String redisKey = url + "-" + ip; |
||||
|
||||
//判断ip是否在黑名单中
|
||||
if (RedisUtil.exist(BLACK_IP_KEY + redisKey)) return 0; |
||||
|
||||
//判断是否超出url的总访问频率
|
||||
RateLimiterHelper helper = limitMap.get(url); |
||||
if (helper == null) return 1; |
||||
if (!helper.getLimiter().tryAcquire(TIME_OUT, MILLISECONDS)) return -1; |
||||
|
||||
//判断此ip是否超出url的访问频率
|
||||
long count = RedisUtil.incrAndExpire(IP_LIMIT_KEY + redisKey, 1); |
||||
if (helper.getIpRate() > count - 1) return 1; |
||||
|
||||
//ip暂时拉黑
|
||||
RedisUtil.set(BLACK_IP_KEY + redisKey, 1, EXPIRE_SECONDS); |
||||
return -2; |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
Instant start = Instant.now(); |
||||
|
||||
initLimitMap(); |
||||
|
||||
Instant end = Instant.now(); |
||||
log.info("路由模块启动成功,耗时:{}毫秒", ChronoUnit.MILLIS.between(start, end)); |
||||
} |
||||
|
||||
private void initLimitMap() { |
||||
limitMap = new ConcurrentHashMap<>(); |
||||
List<ResourceVo> resources = service.getAll(); |
||||
for (ResourceVo resource : resources) { |
||||
RateLimiterHelper helper = new RateLimiterHelper(resource.getTotalRate(), resource.getIpRate()); |
||||
limitMap.put(resource.getUrl(), helper); |
||||
} |
||||
} |
||||
|
||||
public static void updateRate(String url, long totalRate, long ipRate) { |
||||
limitMap.remove(url); |
||||
limitMap.put(url, new RateLimiterHelper(totalRate, ipRate)); |
||||
} |
||||
|
||||
@Autowired |
||||
public void setService(SysResourceService service) { |
||||
this.service = service; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue