SpringMvc 限流之 RateLimiter (比较详细的例子)
以下是非springboot方式的SpringMVC 3.x版本
1 2 3 4 5 6 7 8 9 10 11 |
<bean id="requestLimitInterceptor" class="com.ronghuitec.comm.controller.RequestLimitInterceptor"> <property name="globalRateLimiter" value="1000" /> <property name="urlProperties"> <props> <prop key="/api/**">10</prop> </props> </property> </bean> <mvc:interceptors> <ref bean="requestLimitInterceptor" /> </mvc:interceptors> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
package com.ronghuitec.comm.controller; import com.google.common.base.Joiner; import com.google.common.util.concurrent.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.util.AntPathMatcher; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.util.UrlPathHelper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; public class RequestLimitInterceptor implements HandlerInterceptor, BeanPostProcessor { private Logger logger = LoggerFactory.getLogger(RequestLimitInterceptor.class); private Integer globalRateLimiter = 100; private Map<PatternsRequestCondition, RateLimiter> urlRateMap; private Properties urlProperties; private UrlPathHelper urlPathHelper = new UrlPathHelper(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (urlRateMap != null) { String lookupPath = urlPathHelper.getLookupPathForRequest(request); for (PatternsRequestCondition patternsRequestCondition : urlRateMap.keySet()) { //使用spring DispatcherServlet的匹配器PatternsRequestCondition进行匹配 //spring 4.x版本 //List<String> matches = patternsRequestCondition.getMatchingPatterns(lookupPath); //spring 3.版本 Set<String> matches = patternsRequestCondition.getMatchingCondition(request).getPatterns(); if (!matches.isEmpty()) { if (urlRateMap.get(patternsRequestCondition).tryAcquire(1000, TimeUnit.MILLISECONDS)) { logger.info(" 请求'{}'匹配到mathes {} ,成功获取令牌,进入请求。", lookupPath, Joiner.on(",").join(patternsRequestCondition.getPatterns())); } else { logger.info(" 请求'{}'匹配到mathes {},超过限流速率,获取令牌失败。", lookupPath, Joiner.on(",").join(patternsRequestCondition.getPatterns())); return false; } } } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } /** * 限流的 URL与限流值的K/V 值 * * @param urlProperties */ public void setUrlProperties(Properties urlProperties) { this.urlProperties = urlProperties; } public void setGlobalRateLimiter(Integer globalRateLimiter) { this.globalRateLimiter = globalRateLimiter; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (RequestMappingHandlerMapping.class.isAssignableFrom(bean.getClass())) { if (urlRateMap == null) { urlRateMap = new ConcurrentHashMap<>(); } logger.info("we get all the controllers's methods and assign it to urlRateMap"); RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) bean; Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods(); for (RequestMappingInfo rmi : handlerMethods.keySet()) { PatternsRequestCondition pc = rmi.getPatternsCondition(); //urlRateMap.put(pc, RateLimiter.create(globalRateLimiter)); } if (urlProperties != null) { for (String urlPatterns : urlProperties.stringPropertyNames()) { String limit = urlProperties.getProperty(urlPatterns); if (!limit.matches("^-?\\d+$")) logger.error("the value {} for url patterns {} is not a number ,please check it ", limit, urlPatterns); urlRateMap.put(new PatternsRequestCondition(urlPatterns), RateLimiter.create(Integer.parseInt(limit))); } } } return bean; } } |