001 redis高并发减库存-1DecrProductStockController.java

时间:2024-04-29 07:14:48

decrProductStock.lua

if redis.call('get', KEYS[1]) ~='0'
    then
        return redis.call('decrby',KEYS[1],ARGV[1])
else
     return -1;
end



deleteLock.lua


if redis.call('get', KEYS[1]) == ARGV[1]
    then
        return redis.call('del', KEYS[1])
    else
        return 0
end



package com.example.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 测试高并发减库存
 */
@RestController
@RequestMapping("test")
public class DecrProductStockController {

    @Autowired
    private RedisTemplate redisTemplate;
    private  DefaultRedisScript<Long> decrStockLua;
    private  DefaultRedisScript<Long> deleteLockLua;

    //在本类di注入之后,在初始化的时候,就要加载lua脚本

    @PostConstruct
    public void loadLuaScript(){
        System.out.println("加载lua脚本");

        decrStockLua = new DefaultRedisScript<>();
        decrStockLua.setResultType(Long.class);
        decrStockLua.setScriptSource(new ResourceScriptSource(new ClassPathResource("decrProductStock.lua")));



        deleteLockLua = new DefaultRedisScript<>();
        deleteLockLua.setResultType(Long.class);
        deleteLockLua.setScriptSource(new ResourceScriptSource(new ClassPathResource("deleteLock.lua")));



    }


    /**
     * 1.redis:(1)商品 (2)锁key-(lock_pro_101) value(UUID)
     * 2.能否获得锁 setnx key value ex 10
     * 3.减库存:(1)获得库存get number != null >0   (2)decr number ===>原子性lus脚本
     * 4释放锁(1) if get key == uuid (2) del key ===>原子性lua脚本
     *
     * @param proId
     * @return
     */
    @GetMapping("decrStock/{proId}")
    public String decrStock(@PathVariable("proId") Integer proId){

        System.out.println(Thread.currentThread().getName() + "用户开始抢购商品" + proId);


        //锁的key-value
        String lockKey = "lock_pro_"+proId;
        String lockValue = UUID.randomUUID().toString().replace("-","");
        boolean isGetLock = redisTemplate.opsForValue().setIfAbsent(lockKey,lockValue,10, TimeUnit.SECONDS);

        //商品的key:pro_::101 -product
        //商品库存key: pro_stock::101 -5
        String proKey = "pro_stock_"+proId;

        //获得到锁
        if(isGetLock){
            System.out.println(Thread.currentThread().getName() + "用户获得到了商品的锁"+lockKey);


//            System.out.println(Thread.currentThread().getName()+",获得到了商品的锁"+lockKey);
//           Integer stock = (Integer)redisTemplate.opsForValue().get(proKey);
//            if(stock != null && stock >0){
//                redisTemplate.opsForValue().decrement(proKey);
//            }

            //减库存----LUA
            Long result = (long) redisTemplate.execute(decrStockLua,Arrays.asList(proKey),1);
            if (result == -1){
                System.out.println("库存不足了============");

            }else{
                System.out.println(Thread.currentThread().getName() + "用户已减库存----");
            }





            //释放锁
//            if(redisTemplate.opsForValue().get(lockKey)==lockValue){
//                redisTemplate.delete(lockKey)
//            }
//            System.out.println(Thread.currentThread().getName()+"已释放锁");
//            System.out.println(Thread.currentThread().getName()+"目前的库存>>>");

            //释放锁 ====LUA
            redisTemplate.execute(deleteLockLua,Arrays.asList(lockKey),lockValue);
            System.out.println(Thread.currentThread().getName()+"用户已释放锁");
            System.out.println("****************");


        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            decrStock(proId);
        }



        return "success";
    }


}