zoukankan      html  css  js  c++  java
  • (高并发)防止重复点击,屏蔽多次无效请求的解决方案(优惠劵被重复领取,恶意撸羊毛)

     

    一、问题描述:

      单应用切换至分布式,优惠劵被同一时间同一优惠劵领取多张,比如使用模拟器1s内请求1000次,可能被领取100张。

           以前插入前先查询是否存在,无法有效解决,还是会被撸羊毛。

    二、解决方法:

    1.app前端增加控制,(比如按钮点击后失效);

    2.利用数据库层的事务处理,比如:插入的同时查询是否存在,利用数据库自身的事务管理,同一sql实现

    insert into buyer_collect(buyer_id,goods_id,create_time)
    select #{userId},#{goodsId},now()
    from dual where not exists(select 1 from buyer_collect where buyer_id = #{userId} and goods_id = #{goodsId})
    

      

    3.redis互斥锁(个人推荐,复用性好,稳定性好)

      如果框架还未整合redis请自行查阅处理,这边直接上解决方案:引入jar包,本人使用2.9

          <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
              </dependency>
    

      

    利用jedis类的set方法特性

    nxxx的值只能取NX或者XX,如果取NX,则只有当key不存在是才进行set,如果取XX,则只有当key已经存在时才进行set
    expx的值只能取EX或者PX,代表数据过期时间的单位,EX代表秒,PX代表毫秒。
    time 过期时间,单位是expx所代表的单位。

    public String set(String key, String value, String nxxx, String expx, int time) { this.checkIsInMultiOrPipeline(); this.client.set(key, value, nxxx, expx, time); return this.client.getStatusCodeReply(); }

     编写公共方法,可根据自己的实际情况自行改造,本用例来源于网络,较为成熟稳定(亲测可行)

     1     private static final String LOCK_SUCCESS = "OK";
     2     private static final String SET_IF_NOT_EXIST = "NX";
     3     private static final String SET_WITH_EXPIRE_TIME = "EX";
     4 
     5     /**
     6      * 获取分布式锁
     7      * @param lockKey 锁
     8      * @param requestId 请求标识
     9      * @param expireTime 超期时间
    10      * @return 是否获取成功
    11      */
    12     public  boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
    13         Jedis jedis = null;
    14         try {
    15             jedis = getJedis();
    16             String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    17             if (LOCK_SUCCESS.equals(result)) {
    18                 return true;
    19             }
    20             return false;
    21         } finally {
    22             returnResource(jedis);
    23         }
    24     }
                //Redis缓存 5S内重复请求无效
                Boolean result = null;
                String key = "项目名称+功能模块" + userId + couponId;
                String requestId = UUID.randomUUID().toString();
                try {
                    //获取分布式锁
                    result = redisCacheService.tryGetDistributedLock(key, requestId, 5);
                }catch (Exception e){
                    return new JsonResult(JsonResultCode.FAILURE,"连接redis取数异常","");
                }
                if(!result){
                    return new JsonResult(JsonResultCode.FAILURE,"请勿频繁点击!","");
                }

     附上使用的一小段代码,希望能秒懂解决实际问题

    userId :用户id
    couponId :优惠劵id
    项目名称+功能模块:根据自己项目命名

    结合自己项目框架自行修改,一次编写,复用性好。有效解决项目中类型的问题
  • 相关阅读:
    EC600S连接阿里云
    纪念首次使用vscode+platformio完成点灯全过程
    使用EC600S-CN实现短信收发功能
    基于stm32,通过更换数据存储扇区提升w25q128flash芯片使用寿命
    0.96寸OLED模块-简述如何修改OLED_ShowChar()函数达到修改显示字体大小的目的
    stm32定时器初始化后自动进入一次中断问题
    个人PSP升级作业
    第一个微信小项目
    自己设计大学排名-数据库实践
    自己的第一个网页
  • 原文地址:https://www.cnblogs.com/180308-new-file/p/10479114.html
Copyright © 2011-2022 走看看