基于spring boot商城秒杀,缓解高并发压力
后台结构设计
所用技术:redis缓存,rabbitMQ消息中间件
设计思路
1.系统初始化,从数据库把秒杀商品库存数量和商品的开始和结束的时间加载到redis;redis设置时确保每种商品的Key唯一;
2.进入秒杀页面前进行登陆判断:登陆成功后设置cookie,生成一个随机数TOKEN,保存在cookie中,并保存在redis中。进入秒杀页面前从redis取值,进行判断;
3.进入秒杀页面后,根据商品的id从redis中取到秒杀结束和开始的时间毫秒数;再结合当前的时间与前者比较,返回三个不同状态的值,分别为,”未开始“(返回开始时间),“正在进行中”(返回一个毫秒数),“已结束”;
4.接下来是重点了:秒杀请求的处理,首先还是先判断是否登陆,然后从redis中获取库存,若有库存,则判断是否已经秒杀过,通过从redis中取值,因为生成订单的时候会根据用户的id加“orderid”作为键存储;若未秒杀过,则要先在redis中预减库存;
5.预减库存后,就要入队(rabbitMQ),通过队列可以减少并发访问服务器带来的的压力,还可以解耦;
6.在出队列以后,首先从数据库查看库存(二次保险),若有剩余则下一步,先产生一个事务:预减库存,生成订单(生成一个随机不重复的订单号);然后再生成一个定时器,在十分钟以后,访问数据库订单状态是否已付款,若没付款则redis和数据库的库存都加1;这是防止别人恶意破坏,还可以给那些总是秒杀不下单的用户添加标识,来拒绝请求;若下单成功则修改订单状态;
总结:使用redis和队列都是为了缓解高并发带给数据库和服务器的压力;
然后就是前端的设计
前端主要是秒杀页面的设计,使用springboot建议不使用jsp,而使用html;所以就不能用表达式和jstl了;我们用thymeleaf来操作后台数据;
首先要加上这句话:
如果要获取传过来的值 例如 var remain=[[${remainsecond}]]
好了,先展示一下倒计时的代码和图片效果
timeDown()
function timeDown(){
if(remain*1>0){
$("#button").attr("disabled",false)
var t1 = window.setInterval(function(){
if(remain*1/(3600*24)<1*1){
$("#day").html("00")
if(remain/3600<1){
$("#hour").html("00")
var x1=parseInt(remain/60).toString()
if(x1.length==1){
x1='0'+x1
}
$("#min").html(x1)
if(remain%60==0){
$("#second").html("00")
}
else{
var x=parseInt(remain%60).toString()
if(x.length==1){
x='0'+x
}
$("#second").html(x)
}
}
else{
var x2=parseInt(remain/3600).toString()
if(x2.length==1){
x2='0'+x2
}
$("#hour").html(x2)
var min=parseInt(remain%3600/60)
var sec=parseInt(remain%3600%60)
if(min==0){
$("#min").html("00")
if(sec==0){
$("#second").html("00")
}
else{
sec=sec.toString()
if(sec.length==1){
sec='0'+sec
}
$("#second").html(sec)
}
}
else{
min=min.toString()
if(min.length==1){
min='0'+min
}
$("#min").html(parseInt(min))
if(sec==0){
$("#second").html("00")
}
else{
sec=sec.toString()
if(sec.length==1){
sec='0'+sec
}
$("#second").html(parseInt(sec))
}
}
}
}
else{
var x3=parseInt(remain/(3600*24)).toString()
if(x3.length==1){
x3='0'+x3
}
$("#day").html(x3)
var ho=parseInt(remain%(3600*24))
if(ho==0){
$("#hour").html("00")
$("#min").html("00")
$("#second").html("00")
}
else{
ho=ho.toString();
if(ho.length==1){
ho='0'+ho
}
$("#hour").html(ho)
$("#min").html("00")
$("#second").html("00")
}
}
remain=remain-1
},1000)
}`
<div id="zhuangtai">秒杀状态:</div>
<div th:if="${statue} == 1" class="tai">未开始<i id="nobegin" th:text="${#dates.format(goodsdetail.starttime,'yyyy-MM-dd HH:mm:ss')}"></i></div>
<div th:if="${statue} == 2" class="tai" id="miaoshaing">正在进行中<i id="remaintime"><i id="day"></i>天<i id="hour"></i>时<i id="min"></i>分<i id="second"></i>秒</i></div>
<div th:if="${statue} == 3" class="tai">已结束</div>
</div>
当点击秒杀按钮,若进入队列以后,会返回“排队中“的状态,然后再ajax去查看有没有出队,若出对后,没任何问题则进入订单页面,否则返回“排队中”,再通过轮询执行这个请求,直到秒杀成功或失败为止;
$("#button").click(function(){
killgoods();
})
function killgoods(){
$.ajax({
type:"post",
url:"killgoods/"+goodsdetail.goodsid,
success:function(date){
if(date=="success"){
alert("排队中")
goodsResult(goodsdetail.goodsid)
}
else{
alert(date)
}
}
})
}
//轮询访问秒杀状态,若不成功则一直访问
function goodsResult(goodsid){
$.ajax({
type:"post",
url:"goodsresult/"+goodsdetail.goodsid,
success:function(date){
//秒杀成功,前往订单页面
if(date=="1"){
window.location.href="orderview/"+goodsid
}
//秒杀失败
else if(date=="-1"){
alert("秒杀失败")
}
//正在排队,一排队就访问
else{
setTimeout(goodsResult(goodsid),1000)
}
}
})
}