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

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

作者: 柠檬冰块 | 来源:发表于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