JavaWeb限流QPS简易框架

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);
        }
    }

}
时间: 2024-10-29 05:05:03

JavaWeb限流QPS简易框架的相关文章

Spring Cloud Alibaba | Sentinel: 服务限流高级篇

目录 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 1. 熔断降级 1.1 降级策略 2. 热点参数限流 2.1 项目依赖 2.2 热点参数规则 3. 系统自适应限流 3.1 背景 3.2 系统规则 3.3 原理 3.4 示例 4. 黑白名单控制 4.1 规则配置 4.2 示例 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR

限流从概念到实现

并发数限流并发数限流限制的是同一时刻的并发数,所以不考虑线程安全的话,我们只要用一个int变量就能实现,伪代码如下: int maxRequest=100;int nowRequest=0; public void request(){if(nowRequest>=maxRequest){return ;}nowRequest++;//调用接口try{invokeXXX(); }finally{nowRequest--;}}显然,上述实现会有线程安全的问题,最直接的做法是加锁: int maxR

WebApiThrottle限流框架使用手册

阅读目录: 介绍 基于IP全局限流 基于IP的端点限流 基于IP和客户端key的端点限流 IP和客户端key的白名单 IP和客户端key自定义限制频率 端点自定义限制频率 关于被拒请求的计数器 在web.config或app.config中定义限制策略 获取API的客户端key 存储限流的数据 运行期间更新限制频率 限流的请求日志 用ThrottlingFilter.EnableThrottlingAttribute特性配置限制频率 关于ThrottlingMiddleware限制频率 介绍 为

【Dnc.Api.Throttle】适用于.Net Core WebApi接口限流框架

Dnc.Api.Throttle    适用于Dot Net Core的WebApi接口限流框架 使用Dnc.Api.Throttle可以使您轻松实现WebApi接口的限流管理.Dnc.Api.Throttle支持IP.用户身份.Request Header.Request QueryString等多种限流策略,支持黑名单和白名单功能,支持全局拦截和单独Api拦截. Dnc.Api.Throttle暂时只支持Redis作为缓存和存储库,后续会进行扩展. 开始使用 安装Dnc.Api.Thrott

DRF框架中其他功能:认证、权限、限流

定义视图时,只要视图继承了APIView或其子类,就可以使用DRF框架的认证.权限和限流功能. 当客户端访问API接口时,DRF框架在调用对应的API接口之前,会依次进行认证.权限和限流的操作. 认证Authentication 权限Permissions 限流Throttling 原文地址:https://www.cnblogs.com/oklizz/p/11290731.html

Hystrix分布式系统限流、降级、熔断框架(二)

三.Hystrix容错 ???????Hystrix的容错主要是通过添加容许延迟和容错方法,帮助控制这些分布式服务之间的交互. 还通过隔离服务之间的访问点,阻止它们之间的级联故障以及提供回退选项来实现这一点,从而提高系统的整体弹性.Hystrix主要提供了以下几种容错方法: 资源隔离 熔断 降级 1.资源隔离-线程池 2.资源隔离-信号量 线程池和信号量隔离比较 ? 线程切换 支持异步 支持超时 支持熔断 限流 开销 信号量 否 否 否 是 是 小 线程池 是 是 是 是 是 大 ???????

高并发限流策略

在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹:而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开:而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀.抢购).写服务(如评论.下单).频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流. 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到

鹰眼跟踪、限流降级,EDAS的微服务解决之道

本文主要从服务化的起源开始讲起,围绕EDAS介绍这些年来,随着阿里庞大的电商技术平台流量和并发不断攀升过程中,中间件的微服务技术面临的一系列挑战及解决方法.同时,也会向读者介绍历次双十一背后,EDAS服务化技术的演进历程. 服务化的起源 微服务的解决之道 海量微服务的挑战 关于作者 以下为精彩内容整理: 服务化的起源 阿里巴巴前期技术现状 当时阿里巴巴技术团队规模有500人左右,整个技术网站使用单一War应用,基于传统应用开发架构,业务每年翻倍增长. 我们面临着非常多的问题: 上百人维护一个核心

服务化体系之—限流

服务化体系之—限流 08月 29, 2016 | Filed under 技术 (上)设计篇 在实现算法之前,先临时客串一下产品经理,尝试用最少的字,把“限流”这简单二字所展开的种种需求给说清楚. 1.各种目的 1. 保护每个服务节点. 2. 保护服务集群背后的资源,比如数据库. 3. 避免单个调用者过度使用服务,影响其他调用者. 2. 各种设定维度 2.1. 节点级别 vs 集群级别 如果以保护每个服务节点为目的,可以简单的在本地做节点级别的限流. 但如果以保护服务集群背后的资源为目的,就需要