Java秒杀系统--5.秒杀功能模块

简介

  实现秒杀接口

1.访问秒杀接口

  在秒杀商品详情页面中,点击立即秒杀按钮就可以访问秒杀接口了。

  onclick="getPath()" id="buyButton">开始秒杀</button>:调用getPath(),因为对秒杀接口进行了接口隐藏,所以要先获取path值,然后到url中,才能对秒杀接口进行访问。

2.后端处理获取path的请求,起到隐藏秒杀接口的功能(没用,不如在后端进行时间判断)

  

1 @AccessLimit(seconds = 120,maxCount = 5)
2     @RequestMapping(value = "/getPath",method = RequestMethod.GET)
3     @ResponseBody
4     public Result<String> getPath(Model model,MiaoshaUser user, @RequestParam("goodsId")long goodsId) {
5         String path = MD5Util.md5(UUIDUtil.UUID());
6         redisService.set(PathPrefix.Path,user.getId()+"_"+goodsId,path);
7         return Result.success(path);
8     }

path:一个随机字符串,然后进行一次MD5加密(MD5加密好像可有可无)得到的字符串。

对path使用了redis缓存。

隐藏秒杀接口的目的:为了防止恶意用户提前访问秒杀商品。普通用户在秒杀时间来到时,才可以点击秒杀按钮访问秒杀接口,因为前端设计为在时间未到时秒杀按钮是无效的;恶意用户可能通过查看网页源码得到秒杀接口,在秒杀时间未到时访问秒杀接口。

没用:可以写一个脚本,先获得path变量完成拼接,再访问秒杀接口;还不如在后端添加一个时间判断。

3.前端获的path的值,开始访问秒杀接口

 

 1 success:function(data) {
 2             //获得path参数后,调用秒杀接口函数
 3             go_miaosha(data.data)
 4         },
 5 //调用秒杀接口
 6     function go_miaosha(path) {
 7         $.ajax({
 8             url:"/miaosha/"+path+"/do_miaosha",
 9             type:"POST",
10             data:{
11                 goodsId:$("#goodsId").val(),
12             },
13             success:function (data){
14                 if (data.code==0){
15                     getResult($("#goodsId").val());
16                 }else layer.msg(data.msg)
17             },
18             error:function () {
19                 layer.msg("客户端有误1")
20             }
21         });
22     }

4.后端秒杀接口

  第一个验证:限制某个用户在5秒钟内,不能调用该接口超过5次,并且必须进行登录。

  @AccessLimit(seconds = 5,maxCount = 1,needLogin = true)

  第二个验证:验证url中的path值是否相同,path为一个字符串,被存储在redis中,key由userId和goodsId构成,生存期为60秒。

  访问redis得到其中存储的path值,然后进行比较。

  

1 @AccessLimit(seconds = 120,maxCount = 5)
2     @RequestMapping(value = "/getPath",method = RequestMethod.GET)
3     @ResponseBody
4     public Result<String> getPath(Model model,MiaoshaUser user, @RequestParam("goodsId")long goodsId) {
5         String path = MD5Util.md5(UUIDUtil.UUID());
6         redisService.set(PathPrefix.Path,user.getId()+"_"+goodsId,path);
7         return Result.success(path);
8     }

 

  第三个验证:验证商品的库存数量,利用afterPropertiesSet方法,在spring容器初始化时将数据库中的商品数量缓存到redis中。

  

 1 //该方法在初始化之前执行,将数据库中的商品库存缓存到redis服务器中
 2     private Map<Long,Boolean> goodsStatus = new HashMap<>();
 3     @Override
 4     public void afterPropertiesSet() throws Exception {
 5         List<GoodsVo> goodsList = miaoshaGoodsService.getGoodsList();
 6         for (GoodsVo goods:goodsList
 7              ) {
 8             long id = goods.getId();
 9             long stock = goods.getStockCount();
10             goodsStatus.put(id,false);
11             redisService.set(GoodsPrefix.getGoodsStack,""+id,stock);
12         }
13     }

  当redis中的商品库存被减到零后,所有后来的秒杀请求均被拒绝,并且只会触发一次集合的get操作,连redis操作也不会触发。

  

1     if (goodsStatus.get(goodsId)) {
2             return Result.error(CodeMsg.SECKILL_OVER);
3         }
4         long stock = redisService.decr(GoodsPrefix.getGoodsStack, "" + goodsId);
5         if (stock < 0) {
6             goodsStatus.replace(goodsId, true);
7             return Result.error(CodeMsg.SECKILL_OVER);
8         }

  第四个验证:验证是否包含该用户秒杀该商品的订单,每当完成一次秒杀。都将秒杀订单缓存到redis中,方便快速验证。

1                                                                                                                         //验证4.判断是否有该用户和该商品的订单存在
2         MiaoshaOrder miaoOrder = redisService.get(OrderPrefix.getGoodIdAndUserId, "" + user.getId() + goodsId, MiaoshaOrder.class);
3         if (miaoOrder != null) {
4             return Result.error(CodeMsg.REPEATE_SECKILL);
5         }

  使用rabbitmq将秒杀操作信息封装发送到接收端,然后执行秒杀的动作。使用MiaoshaMessage.class来封装秒杀操作信息。

1 import lombok.Data;
2 
3 @Data
4 public class MiaoshaMessage {
5     private MiaoshaUser miaoshaUser;
6     private long goodsId;
7 }
1      MiaoshaMessage mm = new MiaoshaMessage();
2         mm.setGoodsId(goodsId);
3         mm.setMiaoshaUser(user);
4         mqSender.sendMiaoshaMessage(mm);

  在监听器端还有验证一下数据库中记录的库存,然后再执行秒杀,第五个验证:验证数据库中的商品库存数量。

@RabbitListener(queues = MQConfig.MIAOSHA_QUEUE)
    public void reciveMiaosha(String msg) {
        MiaoshaMessage mm = RedisService.strToBean(msg, MiaoshaMessage.class);
        MiaoshaUser user = mm.getMiaoshaUser();
        long goodsId = mm.getGoodsId();
                                                                                                                        //通过数据库中的数据来判断库存的多少
        GoodsVo goodsVo = miaoshaGoodsService.getGoodsVoById(goodsId);
        int kucun = goodsVo.getStockCount();
        if (kucun == 0) {
            return;
        }
        //进行数据库中的秒杀操作
        miaoshaService.miaoSha(user.getId(), goodsId);
    }

 5.使用MiaoshaService.class中的miaoSha(long useId, long goodsId)方法来完成秒杀工作。

  在该方法上使用@Transactional标签,当事务来处理。总共分为3大步,减库存,下订单,redis缓存秒杀订单。

  

 1 @Transactional
 2     public OrderInfo miaoSha(long useId, long goodsId) {
 3                                                                                                                         //减库存,并设置减库存的结果
 4         boolean reduceResult=miaoshaGoodsService.reduceStock(goodsId);
 5         setReduceResult(goodsId,reduceResult);
 6         miaoshaGoodsService.reduceFMStock(goodsId);
 7                                                                                                                         //下订单
 8         orderInfoService.createOrder(useId,goodsId);
 9         long orderId = orderInfoService.getByUserIdGoodsId(useId,goodsId).getId();
10         MiaoshaOrder miaoshaOrder = new MiaoshaOrder();
11         miaoshaOrder.setMiaoshagoodsId(goodsId);
12         miaoshaOrder.setOrderId(orderId);
13         miaoshaOrder.setUserId(useId);
14         orderInfoService.createMiaoshaOrder(miaoshaOrder);
15                                                                                                                         //为秒杀订单做一个redis缓存
16         redisService.set(OrderPrefix.getGoodIdAndUserId,""+useId+goodsId,miaoshaOrder);
17         return orderInfoService.getByUserIdGoodsId(useId,goodsId);
18     }

至此秒杀功能基本完成。

  

 

  

 

posted on 2020-04-09 21:04  hello,bdiskl  阅读(1481)  评论(0编辑  收藏  举报

导航