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";
}
}