Java Web利用filter实现拦截请求,统计信息、并控制单台机器QPS。
/** * 网络流量控制器 */ public class TrafficFilter implements Filter { private ITrafficStatic trafficStatic; private ITrafficHandler trafficHandler; @Override public void init(FilterConfig filterConfig) throws ServletException { trafficHandler = new AgentTrafficHandler(); trafficStatic = new TrafficStatic(); trafficStatic.init(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // step1: parse bizFunId String bizFunId = request.getParameter("funBizId"); if (StringUtils.isBlank(bizFunId)) { chain.doFilter(request, response); return; } bizFunId = StringUtils.trim(bizFunId); // step2: check whether it should be cached. if (!trafficStatic.shouldLimitTraffic(bizFunId)) { chain.doFilter(request, response); return; } // step3: static the visitor. if (trafficStatic.isOutOfTrafficLimit(bizFunId)) { trafficHandler.handle((HttpServletRequest) request, (HttpServletResponse) response); return; } int visNum = trafficStatic.incAndGetTraffic(bizFunId); if (trafficStatic.isOutOfTrafficLimit(bizFunId, visNum)) { trafficHandler.handle((HttpServletRequest) request, (HttpServletResponse) response); return; } else { chain.doFilter(request, response); return; } } @Override public void destroy() { trafficStatic.destroy(); } }
public interface ITrafficStatic { public void init(); public void destroy(); public boolean shouldLimitTraffic(String bizId); public int incAndGetTraffic(String bizId); public boolean isOutOfTrafficLimit(String bizId, int number); public boolean isOutOfTrafficLimit(String bizId); }
public class TrafficStatic implements ITrafficStatic { private static final AvatarLogger LOGGER = AvatarLoggerFactory.getLogger(TrafficStatic.class); private Map<Integer, Map<String, AtomicInteger>> staticMap = new ConcurrentHashMap<Integer, Map<String, AtomicInteger>>(); private volatile Map<String, Integer> limitMap = new HashMap<String, Integer>(); private volatile Map<String, Integer> tempMap; @Override public void init() { initCleanThread(); initSyncThread(); } @Override public void destroy() { } private void initCleanThread() { new Thread(new Runnable() { @Override public void run() { while (true) { sleep(60 * 1000); // sync pv every 1 min. int version = (int) System.currentTimeMillis() / 1000 - 60; Iterator<Map.Entry<Integer, Map<String, AtomicInteger>>> iterator = staticMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, Map<String, AtomicInteger>> entry = iterator.next(); if (entry.getKey() <= version) { iterator.remove(); } } } // while } }).start(); } private void initSyncThread() { new Thread(new Runnable() { @Override public void run() { while (true) { sleep(60 * 1000); // sync pv every 1 min. if (MapUtils.isNotEmpty(tempMap)) { tempMap.clear(); tempMap = null; } int version = getTimestampVersion(); tempMap = limitMap; limitMap = readConfigFromLion(); for (int i = version; i < (version + 5*60); ++i) { if (!staticMap.containsKey(i)) { staticMap.put(i, new ConcurrentHashMap<String, AtomicInteger>()); } checkAndNewMapEntries(limitMap, staticMap.get(i)); } } // while } }).start(); } private static Map<String, Integer> readConfigFromLion() { try { Map<String, Integer> map = JsonUtils.fromJson(PropertiesLoaderSupportUtils.getProperty("tpfun-promo-web.networktraffic.traffic-config", "{}"), Map.class); return MapUtils.isEmpty(map) ? MapUtils.EMPTY_MAP : map; } catch (Exception e) { LOGGER.error("[networktraffic] error with reading config from lion", e); return MapUtils.EMPTY_MAP; } } private void checkAndNewMapEntries(Map<String, Integer> source, Map<String, AtomicInteger> target) { for (Map.Entry<String, Integer> entry : source.entrySet()) { if (!target.containsKey(entry.getKey())) { target.put(entry.getKey(), new AtomicInteger(0)); } } } private void sleep(long mills) { try { Thread.sleep(mills); } catch (InterruptedException e) { LOGGER.error("[networktraffic] PvCounterBiz threads.sleep error", e); } } private int getTimestampVersion() { return (int) (System.currentTimeMillis() / 1000); } @Override public boolean shouldLimitTraffic(String bizId) { return limitMap.containsKey(bizId); } @Override public int incAndGetTraffic(String bizId) { int ver = getTimestampVersion(); if (!staticMap.containsKey(ver)) { return 1; } Map<String, AtomicInteger> map = staticMap.get(ver); if (MapUtils.isEmpty(map) || !map.containsKey(bizId)) { return 1; } return map.get(bizId).incrementAndGet(); } @Override public boolean isOutOfTrafficLimit(String bizId, int number) { int ver = getTimestampVersion(); if (!limitMap.containsKey(bizId)) { return false; } return (number > limitMap.get(bizId)); } @Override public boolean isOutOfTrafficLimit(String bizId) { int ver = getTimestampVersion(); if (!staticMap.containsKey(ver)) { return false; } Map<String, AtomicInteger> map = staticMap.get(ver); if (MapUtils.isEmpty(map) || !map.containsKey(bizId)) { return false; } return isOutOfTrafficLimit(bizId, map.get(bizId).intValue()); } }
public interface ITrafficHandler { void handle(HttpServletRequest request, HttpServletResponse response); }
public class AgentTrafficHandler implements ITrafficHandler { private AjaxTrafficHandler ajaxTrafficHandler = new AjaxTrafficHandler(); private MobileTrafficHandler mobileTrafficHandler = new MobileTrafficHandler(); @Override public void handle(HttpServletRequest request, HttpServletResponse response) { if (StringUtils.contains(request.getRequestURI(), "ajax")) { ajaxTrafficHandler.handle(request, response); } else { mobileTrafficHandler.handle(request, response); } } }
public class AjaxTrafficHandler implements ITrafficHandler { private static final AvatarLogger LOGGER = AvatarLoggerFactory.getLogger(MobileTrafficHandler.class); private final static String RETURN_JSON = "{code:509, message:'out of max req limit'}"; @Override public void handle(HttpServletRequest request, HttpServletResponse response) { response.setHeader("Content-Type", "application/json;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); response.setStatus(200); try { response.getOutputStream().write(RETURN_JSON.getBytes("UTF-8")); } catch (IOException e) { LOGGER.error(e); } } }
public class MobileTrafficHandler implements ITrafficHandler { private static final AvatarLogger LOGGER = AvatarLoggerFactory.getLogger(MobileTrafficHandler.class); private final static String RETURN_HTML = "<html>" + "<head>" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />" + "\n<meta charset='utf-8'>" + "<title>大众点评网</title>" + "</head><body>" + "<center>挤爆了、请稍等...</center>" + "</body>" + "<script type='text/javascript'>(function(){function reload(){ window.location.reload(); setTimeout(reload, 1500);} setTimeout(reload, 1500);})();" + "</script>" + "</html>"; @Override public void handle(HttpServletRequest request, HttpServletResponse response) { response.setHeader("Content-Type", "text/html;charset=utf-8"); response.setCharacterEncoding("utf-8"); response.setStatus(200); try { response.getOutputStream().write(RETURN_HTML.getBytes("UTF-8")); } catch (IOException e) { LOGGER.error(e); } } }