美文网首页
在分布式高并发场景下,领取百万的优惠码,怎样保证每个用户只能领取

在分布式高并发场景下,领取百万的优惠码,怎样保证每个用户只能领取

作者: 柠檬冰块 | 来源:发表于2019-04-02 11:21 被阅读0次

    基本思路是:锁+幂等性

    具体实现:

    使用redis的decr (对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。这个操作最大支持在64位有符号的整型数字。)可以实现原子性的递增递减操作控制优惠码不超送,然后给每个用户维护一个userid+优惠码活动的key保证幂等性,只要redis存在这种key,那就代表已经领取了,具体的优惠码分发可以异步执行。为了避免竞争(同一个用户,多个设备同时领取):

    获取锁 领取优惠码(伪代码)

    1,分布式锁方法

    public boolean acquireLock(String lock) {

    // 1. 通过SETNX试图获取一个lock

        boolean success;

        Jedis jedis =jedisPool.getResource();

        //强制过期时间:60 秒

        int expired =60 *1000;

        long value = System.currentTimeMillis() + expired;

        long acquired = jedis.setnx(lock, String.valueOf(value));

        if (1 == acquired) {   //setnx 成功,则成功获取一个锁

            success =true;

        }else { //setnx 失败,说明锁仍然被其他对象保持,检查其是否已经超时

             // 获取覆盖旧值之前获取到的旧值

            long oldValue = Long.valueOf(jedis.get(lock));

            //检查是否超时(对比锁是否超时)

            if (oldValue < System.currentTimeMillis()) {  //超时,进行锁覆盖 

         

                // 获取覆盖值之后的获取到的旧值

               String getOldValue = jedis.getSet(lock, String.valueOf(value));

                if (Long.valueOf(getOldValue) == oldValue) { // 获取锁成功(覆盖旧值之前获取到的旧值 和 覆盖值之后的获取到的旧值 相同 则本次获锁成功,否则 失败已经被别的对象获取到)

                   success =true;

                }else { // 已被其他进程捷足先登了

                    success =false;

                }

              }else { //未超时,则直接返回失败

                success =false;

               }

    }

    jedisPool.close();

        return success;

    }

    2.业务代码(优惠码领取发放)

    @Test

        public void redisAcquireLockLock(User user, String eventsPromoCodeMark) {

            Jedis jedisResources = jedisPool.getResource();

            //1,活动上线前设置优惠码总量一百万:jedisResources.set(promoCodeNumberKey, "1000000");

            //设置本次活动优惠码锁(保证幂等性):userId + 优惠码活动的key

            String lock = user.getId() + eventsPromoCodeMark;

            //设置优惠码总量key

            String promoCodeNumberKey = eventsPromoCodeMark + "promoCodeNumber";

            //设置用户优惠码领取key

            String userKey = lock + "receiveCoupon";

            //优惠码领取完了

            boolean promoCode = jedisResources.exists(promoCodeNumberKey) &&

                    StringUtils.isNotBlank(jedisResources.get(promoCodeNumberKey)) &&

                    0L < Long.valueOf(jedisResources.get(promoCodeNumberKey));

            if (!promoCode) {

                System.out.println("优惠码已经领取完了");

                jedisResources.close();

                return;

            }

            //为了避免竞争(同一个用户,多个设备同时领取).使用Redis.setNX() 实现分布式锁(重复数据插入可用其来实现排他锁)

            boolean flag = acquireLock(lock);

            if (flag) {

                //1,获取锁成功,进行用户已领取标记,查询用户是否已经领取过

                Boolean isExists = jedisResources.exists(userKey);

                if (!isExists) {

                    //2,先使用redis的decr可以实现原子性的递增递减操作控制优惠码不超送,

                    Long success = jedisResources.decr(promoCodeNumberKey);

                    // 先减库存后发码(减库存后返回的现有库存数量大于等于0说明本次抢码成功,再进行发送优惠码,否则库存已经空了就不进行发送优惠码)

                    if (success >= 0L) {

                        //3,再进行优惠码分发(可进行MQ进行发放优惠码,如果减库存成功,发放失败,进行发放补偿性操作)

                        jedisResources.set(userKey, "received");

                        System.out.println("领取成功");

                    }

                } else {

                    System.out.println("已经领取过了");

                }

            } else {

                System.out.println("请重试");

            }

            jedisResources.close();

        }

    相关文章

      网友评论

          本文标题:在分布式高并发场景下,领取百万的优惠码,怎样保证每个用户只能领取

          本文链接:https://www.haomeiwen.com/subject/hmewbqtx.html