Redis分布式锁实现简单秒杀功能

这版秒杀只是解决瞬间访问过高服务器压力过大,请求速度变慢,大大消耗服务器性能的问题。

主要就是在高并发秒杀的场景下,很多人访问时并没有拿到锁,所以直接跳过了。这样就处理了多线程并发问题的同时也保证了服务器的性能的稳定。

接下来我们使用redis的分布式锁来进行枷锁处理:

我们可以在进入下单的方法后将核心的方法加锁,然后离开后进行解锁

主要三步:

加锁

核心方法

解锁

首页分布式加锁解锁工具类:

@Component
public class RedisLock {
    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加锁
     * @param key
     * @param value 当前事件+超时事件
     * @return
     */
    public boolean lock(String key,String value){
        //加锁成功
        if (redisTemplate.opsForValue().setIfAbsent(key,value)){
            return true;
        }
        //假如currentValue=A先占用了锁  其他两个线程的value都是B,保证其中一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key);
        //锁过期  防止出现死锁
        if (!StringUtils.isEmpty(currentValue) &&
                Long.parseLong(currentValue) < System.currentTimeMillis()){
            //获取上一步锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) &&
                    oldValue.equals(currentValue)){
                return true;
            }
        }
        return false;
    }

    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key,String value){
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) &&
                    currentValue.equals(value)){
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e){
            logger.error("【redis分布式锁】 解锁异常,{}",e);
        }
    }
}

  

具体使用的逻辑代码功能:

@Service
public class SecKillService {
    private static Logger logger = LoggerFactory.getLogger(SecKillService.class);
    /** 超时时间 */
    private static final int TIMEOUT = 10000;

    @Autowired
    private RedisLock redisLock;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private RestTemplate restTemplate;


    /**
     * @Description: 秒杀商品接口
     * @param weddingExpoAppoint
     * @return JsonObject
     * @exception
     * @author mazhq
     * @date 2018/11/18 13:46
     */
    private JsonObject seckillProduct(long productId) {
        long time = System.currentTimeMillis() + TIMEOUT;
        String stockKey = RedisKeysManager.getWeddingExpoSeckillStockKey(productId);
        //加锁
        String lockKey = "weddingExpo:seckill:"+productId;
        if (!redisLock.lock(lockKey,String.valueOf(time))){
            return BaseCode.retCode(100, "没抢到,换个姿势再来一遍");
        }

        String stockNumStr = redisClient.getStr(stockKey);
        int stockNum = 0;
        if(StringUtils.isNotBlank(stockNumStr)){
            stockNum = Integer.valueOf(stockNumStr);
        }
        JsonObject respJson = BaseCode.retCode(ResultCode.failure);
        if (stockNum == 0) {
            //库存不足
            return BaseCode.retCode(100, "商品已经被抢光了,请留意下次活动");
        } else {

            try {
                String resp = doseckill(productId);
                if(null != resp){
                    respJson = new JsonObject(resp);
                    if(respJson.getInteger("retcode") == 0){
                        redisClient.increment(stockKey, -1);
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        //解锁
        redisLock.unlock(lockKey, String.valueOf(time));
        return respJson;
    }


}

  

主要功能描述就是:

秒杀商品时候先加锁,如果没有获取到锁就释放请求。 

加锁后先进行库存判断如果不足释放请求。

进行秒杀下单流程,如果成功库存做减一操作。

最后释放分布式锁。

这样简单的分布式锁处理秒杀功能的方法就搞定了。这种只是处理高并发下多个请求如果有人在秒杀后面的直接不需排队直接释放请求,解放服务器压力(处理流程时间较短,高并发下没有排序要求)。

如果要根据请求时间进行排序,这个方式还需借助队列处理。

猜你喜欢

转载自www.cnblogs.com/owenma/p/10118935.html