DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<script src="webjars/jquery/3.6.0/jquery.js">script>
<script type="text/javascript">
$(document).ready(function(){
$("#secKill").click(function(){
let prodId=$("#prodId").val();
$.post(
"/test/secKill",
{prodId:prodId},
function(data){
if (!data){
alert("来晚了");
}
}
);
});
});
script>
head>
<body>
<input type="text" id="prodId">
<input type="button" value="秒杀" id="secKill" >
body>
html>
@RestController
@RequestMapping("/test")
public class SecKillController {
@Autowired
SecKillService secKillService;
@RequestMapping("/secKill")
public boolean secKill(String prodId){
if (prodId == null || "".equals(prodId)){
return false;
}
Random random = new Random();
//随机用户ID
int uid = random.nextInt(50000);
return secKillService.secKillByScript(prodId,String.valueOf(uid));
}
}
@Service
public class SecKillService {
@Autowired
RedisTemplate redisTemplate;
public boolean secKillByScript(String proId,String uId){
if ("".equals(proId) || null == proId ||"".equals(uId) || null == uId){
return false;
}
DefaultRedisScript redisScript = new DefaultRedisScript();
//加载字符串格式脚本
// redisScript.setScriptText();
//加载文本格式脚本
redisScript.setLocation(new ClassPathResource("secKill.lua"));
redisScript.setResultType(String.class);
List<String> list = new ArrayList<>();
list.add(uId);
list.add(proId);
Object execute = redisTemplate.execute(redisScript, new StringRedisSerializer(),
new StringRedisSerializer(), list, uId, proId);
if ("0".equals(String.valueOf(execute))){
System.out.println("未开始");
}else if("1".equals(String.valueOf(execute))){
System.out.println("已抢光");
}else if("2".equals(String.valueOf(execute))){
System.out.println("已秒杀");
}else if("3".equals(String.valueOf(execute))){
System.out.println("秒杀成功");
}
return true;
}
}
在lua脚本中,有两个全局的变量,是用来接收redis应用端传递的键值和其它参数的,分别为KEYS、ARGV。
在应用端传递给KEYS时是一个数组列表,在lua脚本中通过索引方式获取数组内的值。
在应用端,传递给ARGV的参数比较灵活,可以是多个独立的参数,但对应到Lua脚本中是,统一用ARGV这个数组接收,获取方式也是通过数组下标获取
redisTemplate.execute(redisScript, new StringRedisSerializer(),
new StringRedisSerializer(), list, uId, proId);
就拿 SecKillService 中的举例,参数列表中的 list就是有 KEYS 接收;而后面的参数( uId、proId )由 ARGV 接收
secKill.lua
放在 /resources 目录下
-- KEYS[1] =list(0)
local userid=KEYS[1];
-- KEYS[2] =list(1)
local prodid=KEYS[2];
local uidKey="prod:user:"..userid;
local proKey="prod:kc:"..prodid;
--redis.log(redis.LOG_NOTICE,uidKey);
--判断是否有库存,没有表示未开启秒杀
local userExists=redis.call("exists",proKey);
if tonumber(userExists)==0 then
return "0";
end
--判断库存是否足够,不够停止秒杀
local num=redis.call("get",proKey);
--redis.log(redis.LOG_NOTICE,proKey);
if tonumber(num) <=0 then
return "1";
end
--判断用户是否已参与过秒杀活动
local isMember=redis.call("sisMember",uidKey,userid);
if tonumber(isMember)==1 then
return "2";
else
redis.call("decr",proKey);
redis.call("sadd",uidKey,userid);
end
return "3";
application.properties
server.port=8001
#redis配置
spring.redis.host=xxx.xxx.xxx.xx
spring.redis.port=6379
spring.redis.password=redis123
spring.redis.database=0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=1
spring.redis.lettuce.pool.max-idle=50
spring.redis.lettuce.pool.min-idle=0
#redis集群配置
#spring.redis.cluster.nodes=192.168.117.130:6379,192.168.117.130:6380,192.168.117.130:6381,192.168.117.130:6382,192.168.117.130:6383,192.168.117.130:6384,192.168.117.130:6385,192.168.117.130:6386,192.168.117.130:6387
#spring.redis.cluster.max-redirects=3
#spring.redis.jedis.pool.max-active=20
#spring.redis.jedis.pool.max-wait=1
#spring.redis.jedis.pool.max-idle=50
#spring.redis.jedis.pool.min-idle=0
#spring.redis.timeout=60000
#spring.redis.sentinel.master=mysentinel
#spring.redis.sentinel.nodes=192.168.117.130:6379,192.168.117.130:6380,192.168.117.130:6381
pom.xml
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.5.6version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
<version>2.5.6version>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.6.0version>
dependency>
通过 linux 中的 ab 测试接口
ab -n 2000 -c 200 -p ~/postfile -T application/x-www-form-urlencoded http://192.168.43.99:8001/test/secKill
- -n :总访问次数
- -c :每次的并发量
- -p :保存参数的文件
- -T :请求类型
postfile
prodId=0001&
或者
ab -n 2000 -c 200 -T application/x-www-form-urlencoded http://192.168.43.99:8001/test/secKill?prodId=0001