验证中...
Languages: Java
Categories: 常用工具包
Latest update 2019-03-07 11:39
SpringCloud Zuul 限流控制
Raw Copy
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import com.alibaba.fastjson.JSONObject;
import com.jimi.app.api.IApiAppFunsApi;
import com.jimi.app.dto.output.ApiAppFunsOutputDto;
import com.jimi.app.dto.output.ApiOpenFunctionsOutputDto;
import com.jimi.framework.context.SpringContextHolder;
import com.jimi.framework.utils.DateUtil;
import com.jimi.gateway.exception.GatewayErrorCode;
import com.jimi.gateway.utils.Constants;
import com.jimi.gateway.utils.ResultModel;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
/**
* @FileName RouteLimitFilter.java
* @Description: 路由前限流,采用redis过期机制控制,使用app_key和method进行秒,天维度控制
*
* @Date 2017年4月24日 上午9:27:49
* @author li.shangzhi
* @version 1.0
*/
public class RouteLimitFilter extends ZuulFilter {
private static final int FILTER_ORDER = 0;
private final Logger logger = LoggerFactory.getLogger(RouteLimitFilter.class);
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
String routeId = String.valueOf(ctx.get("proxy"));
if (!Constants.OPEN_API_NAMESPACE.equals(routeId)) {
return false;
}
return ctx.getRouteHost() != null && ctx.sendZuulResponse() && ctx.getBoolean(Constants.IS_NEXT_FILTER, true);
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
ApiOpenFunctionsOutputDto outputDto = (ApiOpenFunctionsOutputDto) ctx.get(Constants.OPEN_FUNCTION);
if (outputDto.getLimit()) { // 限流开关开启
HttpServletRequest requeset = ctx.getRequest();
String method = requeset.getParameter(Constants.METHOD);
String appKey = requeset.getParameter(Constants.APP_KEY);
String funId = outputDto.getId();
IApiAppFunsApi apiAppFunsApi = SpringContextHolder.getBean(IApiAppFunsApi.class);
ApiAppFunsOutputDto apiAppFunsOutputDto = apiAppFunsApi.getByAppFun(appKey, funId);
if (null != apiAppFunsOutputDto) {
// 每秒并发数
int perSecond = apiAppFunsOutputDto.getReqPerSecond();
String secondKey = "jimiOpenApi.limitFilter.second:" + appKey + "_" + method;
boolean isFilter = filterOper(secondKey, perSecond, 1000, TimeUnit.MILLISECONDS);
if (isFilter) {
logger.info("【请求限流】用户AppKey:{},请求方法:{},每秒请求频率超过{},限制访问。redis Key:{}", appKey, method, perSecond, secondKey);
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(200);// 返回错误码
ctx.setResponseBody(JSONObject.toJSONString(new ResultModel(GatewayErrorCode.REQ_RATE_TOO_HIGH_S_ERROR)));
ctx.set(Constants.IS_NEXT_FILTER, false);// 后续的过滤器是否继续执行
return null;
}
// 每天请求数
int perDay = apiAppFunsOutputDto.getReqPerDay();
// 得到当天
int curDay = DateUtil.getCurDay();
String dayKey = "jimiOpenApi.limitFilter.day:" + curDay + "_" + appKey + "_" + method;
long times = DateUtil.getResidueDay();
isFilter = filterOper(dayKey, perDay, times, TimeUnit.MILLISECONDS);
if (isFilter) {
logger.info("【请求限流】用户AppKey:{},请求方法:{},每天请求频率超过{},限制访问。redis Key:{}", appKey, method, perDay, dayKey);
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(200);// 返回错误码
ctx.setResponseBody(JSONObject.toJSONString(new ResultModel(GatewayErrorCode.REQ_RATE_TOO_HIGH_D_ERROR)));
ctx.set(Constants.IS_NEXT_FILTER, false);// 后续的过滤器是否继续执行
return null;
}
} else {
logger.warn("请求方法:{},查找限流接口失败,app_key:{},fun_id:{}", method, appKey, funId);
}
} else {
logger.info("请求方法:{},限流开关:{}", outputDto.getMethod(), outputDto.getLimit());
}
ctx.set(Constants.IS_NEXT_FILTER, true);
return null;
}
@Override
public String filterType() {
return Constants.ROUTE_FILTER;
}
@Override
public int filterOrder() {
return FILTER_ORDER;
}
private boolean filterOper(String uniqueKey, long limits, long times, TimeUnit unit) {
if (limits == 0 || times == 0) {
return false;
}
try {
logger.info("操作的redis Key为:{}", uniqueKey);
RedisTemplate<String, String> redisTemplate = SpringContextHolder.getBean("redisTemplate");
BoundZSetOperations<String, String> boundZSetOperations = redisTemplate.boundZSetOps(uniqueKey);
Set<String> redisStrings = boundZSetOperations.range(0, boundZSetOperations.size());
AtomicLong count = new AtomicLong(0);
if (redisStrings != null && redisStrings.size() > 0) {
Object[] objects = redisStrings.toArray();
count.set(Long.parseLong(String.valueOf(objects[objects.length - 1])));
}
if (count.get() > limits) {
// 超出限制,开启禁用
Long expries = boundZSetOperations.getExpire();
if (expries != null && expries > 0) {
return true;
}
}
long seq = count.incrementAndGet();
boundZSetOperations.add(String.valueOf(seq), seq); // 填充并计数
boundZSetOperations.expire(times, unit); // 设置过期时间
} catch (Exception e) {
logger.error("控制api被调用频率redis发生异常:uniqueKey=" + uniqueKey, e);
}
return false;
}
}

Comment list( 0 )

You need to Sign in for post a comment

Help Search

183227_9af5e6a8_1826025 111910_4d91f001_1826025