springmvc實現網站限流
阿新 • • 發佈:2019-01-25
輔助類,用於儲存每個請求的訪問數
public class AccessCounts { public static final String CALLPATH = "AccessCounts.CALLPATH"; private ConcurrentHashMap<Object, AtomicInteger> map = new ConcurrentHashMap<Object, AtomicInteger>(); private AccessCounts() { } private static class Instance { static AccessCounts counts = new AccessCounts(); } public static AccessCounts getInstance() { return Instance.counts; } public int get(Object key) { AtomicInteger counter = map.get(key); if (counter == null) { counter = new AtomicInteger(0); map.put(key, counter); } return counter.intValue(); } public int incrementAndGet(Object key) { AtomicInteger counter = map.get(key); if (counter == null) { counter = new AtomicInteger(0); map.put(key, counter); } return counter.incrementAndGet(); } public int decrementAndGet(Object key) { AtomicInteger counter = map.get(key); if (counter == null) { return 0; } return counter.decrementAndGet(); } public String status(){ return map.toString(); } }
攔截器
/** *限流攔截器 **/ public class InServiceAccessInterceptor extends HandlerInterceptorAdapter { private static final Logger logger = Logger.getLogger(InServiceAccessInterceptor.class); private ConcurrentHashMap<String, Integer> config = new ConcurrentHashMap<String, Integer>(); // setting private int defaultLimit = 100; private String configUrl = null; private int loadInterval = 1, loadDelay=5; private boolean valid; public InServiceAccessInterceptor(int defaultLimit, String configUrl, int loadInterval, int loadDelay, boolean valid) { this.defaultLimit = defaultLimit; this.configUrl = configUrl; this.loadInterval = loadInterval; this.loadDelay = loadDelay; this.valid = valid; if(valid){ task(); } } //before the actual handler will be executed @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(valid){ if(handler instanceof HandlerMethod){ HandlerMethod handlerMethod = (HandlerMethod) handler; String callpath = handlerMethod.getMethod().getDeclaringClass().getSimpleName()+"."+handlerMethod.getMethod().getName(); System.out.println(callpath); int counter = AccessCounts.getInstance().get(callpath); boolean limit = limit(callpath, counter); if (limit) { throw new IllegalAccessException("Flowing Limit." + callpath + "=" +counter ); } MDC.put(AccessCounts.CALLPATH, callpath); AccessCounts.getInstance().incrementAndGet(callpath); } } return true; } //after the handler is executed @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if(valid){ Object callpath = MDC.get(AccessCounts.CALLPATH); if (null != callpath) { AccessCounts.getInstance().decrementAndGet( callpath); } MDC.remove(AccessCounts.CALLPATH); } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } private boolean limit(String callpath, int counter) { Integer obj = config.get(callpath); int limit = defaultLimit; if (obj != null) { limit = obj.intValue(); } if (logger.isDebugEnabled() ) { logger.debug("check callpath:" + callpath + " limit:" + limit); } if (counter >= limit) { logger.warn("the call[" + callpath + "] is over limit:" + limit + " counter:" + counter); return true; } return false; } public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + (param == null ? "" : "?" + param); URL realUrl = new URL(urlNameString); // 開啟和URL之間的連線 URLConnection connection = realUrl.openConnection(); // 設定通用的請求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立實際的連線 connection.connect(); // 獲取所有響應頭欄位 /*Map<String, List<String>> map = connection.getHeaderFields(); for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); }*/ // 定義 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { logger.error("request url fail="+url, e); } // 使用finally塊來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e) { } } return result; } public void task() { TimerTask task = new TimerTask() { @Override public void run() { if (null == configUrl ) return; String text = sendGet(configUrl, null); logger.info("load config:" + text ); if ( null == text || "".equals(text.trim())) return; text = text.replaceAll("[\\{\\}\"\n]", ""); config.clear(); for (String line : text.split(",")) { String fields[] = line.split(":"); if ( fields.length < 2) continue; try { config.put(fields[0].trim(), Integer.valueOf(fields[1].trim())); } catch (Exception e) { logger.error("load config fail.", e); } } } }; Timer timer = new Timer(); long delay = 1000 * loadDelay; long intevalPeriod = 1000 * loadInterval; // schedules the task to be run in an interval logger.info("Task setting delay:" + delay + " intevalPeriod:" + intevalPeriod); timer.scheduleAtFixedRate(task, delay, intevalPeriod); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(60*1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("AccessCounts status:" + AccessCounts.getInstance().status()); } } }).start(); } }
xml配置
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <beans:bean class="xxx.xxx.xxx.intercept.InServiceAccessInterceptor"> <beans:constructor-arg index="0" value="1000" /> <!-- defaultLimit 預設設定 --> <beans:constructor-arg index="1" value="http://localhost/web.limit" /> <!-- configUrl --> <beans:constructor-arg index="2" value="60" /> <!-- loadInterval unit : second --> <beans:constructor-arg index="3" value="5" /> <!-- loadDelay unit : second --> <beans:constructor-arg index="4" value="false" /> <!-- valid true: used --> </beans:bean> </mvc:interceptor> </mvc:interceptors>
http://localhost/web.limit檔案
{"IndexController.index":500,
"ProductController.searchProducts":500
}