此文章是vip文章,如何查看?  

1,点击链接获取密钥 http://nicethemes.cn/product/view29882.html

2,在下方输入文章查看密钥即可立即查看当前vip文章


Redis实现计数器---接口防刷

  • 时间:
  • 浏览:
  • 来源:互联网

强烈推荐一个大神的人工智能的教程:http://www.captainbed.net/zhanghan

【前言】       

    刚刚过去的双十一,大家有没有剁手,紧接着双十二马上又来临;看到全民大抢购的时候,做为一名技术人,不免的会有些职业病,总会好奇抢购秒杀等等背后的技术。

        正好最近自己在做项目的时候需要防刷,自己在做完后做了些小测试,在这里与诸君共享!

【探索】

        ·唠叨唠叨

             1、最近在做现金贷相关的项目,之前线上项目有的接口被刷过;新做一个功能,吸取之前的教训,如何做到接口不被刷?

             2、在和产品进行确认后,我们会根据用户的id等唯一标识来做相关的限制,根据不同的业务做不同的限制。

        ·前置说明

             1、本次用到的压力测试工具是大名鼎鼎的---jmeter(jmeter官网下载地址),jmeter使用教程---Jmeter对HTTP请求压力测试、并发测试的简单使用方法;

             2、Redis相关命令---Redis 命令参考;

             3、需求:每个用户每天访问某接口的次数不超过20次;下面写了两版代码。    

        ·处女版

             1、相关代码:

                 (1)测试类代码:

    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public ResponseEntity<Object> test(
            HttpServletRequest request) throws Exception {
        //访问次数校验
        boolean flag = RedisBusiness.isOverMaxVisitTimes1("test:test:1", 1, 20);
        String result = "";
        if (flag) {  //超过限制
            result = "zhanghan---is over";
        } else { //未超限制
            result = "zhanghan---not over";
        }
        System.out.println(result);
        return response201(result);
    }

                 (2)封装的判断次数是否超的工具类:

   /**
     * 是否超过访问次数
     *
     * @param key      Redis的key
     * @param days     统计几天之内
     * @param maxTimes 最大访问次数
     * @return
     * @throws BusinessException
     */
    public static boolean isOverMaxVisitTimes(String key, int days, int maxTimes) throws BusinessException {
        //打印入参
        LoggerUtil.controllerInfo("RedisBusiness>>>isOverMaxVisitTimes; param: key:" + key + "; days:" + days + "; maxTimes:" + maxTimes);
        try {
            //先判断Redis中是否有该key值
            if (RedisHelper.exists(key)) {
                //取出访问次数
                int times = Integer.parseInt(RedisHelper.get(key));
                //判断访问是否大于最大次数
                if (times >= maxTimes) {
                    LoggerUtil.controllerInfo("RedisBusiness>>>isOverMaxVisitTimes; key:" + key + " times over max times;times:" + times);
                    return true;
                }
                //若不大于则将访问次数加1
                RedisHelper.incr(key, 1L);
            } else {  //若没有则创建并设置失效时间
                RedisHelper.set(key, "1", days, TimeUnit.DAYS);
            }
        } catch (Exception e) {
            LoggerUtil.controllerInfo("RedisBusiness>>>isOverMaxVisitTimes; get visit times Exception; key:" + key + "result:" + e.getMessage());
            throw new BusinessException(CommonEnum.EXCEPTION_OPENUPLOAD_COMMON_DB);
        }
        return false;
    }                                        
                                        

        ·重构版

             1、相关代码

                 (1)测试类代码---同处女版

                 (2)封装的判断次数是否超的工具类:

    /**
     * 是否超过访问次数
     *
     * @param key      Redis的key
     * @param days     统计几天之内
     * @param maxTimes 最大访问次数
     * @return
     * @throws BusinessException
     */
    public static boolean isOverMaxVisitTimes(String key, int days, int maxTimes) throws BusinessException {
        //打印入参
        LoggerUtil.controllerInfo("RedisBusiness>>>isOverMaxVisitTimes; param: key:" + key + "; days:" + days + "; maxTimes:" + maxTimes);
        try {
            //先对访问次数进行加1
            long visitTimes = RedisHelper.incr(key, 1L);
            if (visitTimes == 1) {  //代表是第一次访问,设置超时时间
                RedisHelper.expire(key, days, TimeUnit.DAYS);
                return false;
            } else if (visitTimes > maxTimes) {  //访问次数超过最大次数
                return true;
            } else {  //访问次数未超过最大次数
                return false;
            }
        } catch (Exception e) {
            LoggerUtil.controllerInfo("RedisBusiness>>>isOverMaxVisitTimes; get visit times Exception; key:" + key + "result:" + e.getMessage());
            throw new BusinessException(CommonEnum.EXCEPTION_OPENUPLOAD_COMMON_DB);
        }
    }

          ·测试

             1、测试环境:

                (1)Windows10---64位  处理器---CORE I7  内存---8G

                (2) jemeter 版本---V3.3

                (3)程序直接在Idea---2017.1版

             2、Jmeter相关设置:

                (1)线程组设置:

 

                (2)HTTP请求的设置:

 

             3、测试结果(将控制台的打印结果粘贴到word中做统计,访问次数可以通过搜索 zhanghan---not over 的次数来得知):

                (1)处女版---并发100测试结果:

                (2)重构版---并发100测试结果:

                (3)处女版---并发1000测试结果:

 

                (4)重构版---并发1000测试结果:                 

          ·分析

             通过压测的结果可以很容易看出重构版能够达到要求,这是实验结论,有了实验结论还有回归原理,从原理上来说明这一点。

             1、Redis是单线程,我接触的很多人对这个概念有误解,我目录理解所谓单线程是指其在执行一个命令时是原子的不会被其他的命令所打断。

             2、处女版分图解:

 

             3、重生版图解:

             4、大家可以结合Redis单线程与处女版和重生版的图解,自己理解一下其中的蕴味。

【总结】

         1、自测是阿猿的基本素质;

         2、充满好奇,不断重构代码;

         3、不管人丑或美,都应多看书---最近在看开涛大神的---《亿级流量网站架构核心技术+跟开涛学搭建高可用高并发系统》,值得一读。

本文链接http://element-ui.cn/news/show-576827.aspx