基于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}]]
好了,先展示一下倒计时的代码和图片效果
基于spring boot商城秒杀,缓解高并发压力

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)
				}
			}
		})
		}