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

  • 21
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当年的春天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值