秒杀场景实战分析

本文详细分析了秒杀场景的实战流程,包括使用redis缓存和分布式锁确保数据一致性,利用lua脚本进行库存扣减,以及通过rabbitMQ进行订单削峰填谷。重点介绍了秒杀接口和正式提交订单接口的控制层及业务层实现,并提供了分布式锁工具类的伪代码。后续将探讨更多实施细节,如消息队列处理和支付接口等。
摘要由CSDN通过智能技术生成

秒杀场景实战分析流程图:

核心技术方案,通过redis缓存和分布式锁、lua脚本保证一致性、rabbitMq削峰填谷正式提交订单,查询商品详情已经分享过,此篇主要分享秒杀和正式下单功能。

1、秒杀接口:

控制层:

  /**
     * 秒杀接口,这里对redis做了预减库存,并把秒杀了的用户做了标识缓存
    */
    @PostMapping("kill")
    public HttpResponseBody kill(int killId) {
        KillGoodsSpecPriceDetailVo killGoods = killGoodsService.detail(killId);
        if (killGoods.getBegainTime().getTime() > System.currentTimeMillis()){
            return HttpResponseBody.failResponse("抢购还未开始");
        }
        if (killGoods.getEndTime().getTime() < System.currentTimeMillis()){
            return HttpResponseBody.failResponse("抢购已结束");
        }
        if (!killGoodsService.secKillByLock(killId,getSessionUserId())){
            log.info("----抢购失败----");
            return HttpResponseBody.failResponse("抢购失败");
        }
        return HttpResponseBody.successResponse("ok",  killGoods);
    }

业务层:

 public boolean secKillByLock(int killId, String userId) {
        //判断用户是否已经秒杀过
        Boolean member = redisTemplate.opsForSet().isMember(KillConstants.KILLED_GOOD_USER + killId, userId);
        if (member) {
            logger.info("---------userId:" + userId + "----has secKilled");
            return false;
        }
        String killGoodCount = KillConstants.KILL_GOOD_COUNT + killId;
        //返回的数值,执行了lua脚本
        Long stock = stock(killGoodCount, 1, STOCK_LUA);
        if (stock == UNINITIALIZED_STOCK) {
            Timer timer = null;
            RedisLock redisLock = new RedisLock(redisTemplate, REDIS_LOCK);
            try {
                //如果竞争锁成功  如果其他线程没竞争锁成功,这里是阻塞的
                if (redisLock.tryLock()) {
                    //锁续命
                    timer = continueLock(REDIS_LOCK);

                    stock = stock(killGoodCount, 1, STOCK_LUA);
                    if (stock == UNINITIALIZED_STOCK) {
                        KillGoodsPrice killGoodsPrice = iKillSpecManageService.selectByPrimaryKey(killId);
                        redisTemplate.opsForValue().set(killGoodCount, killGoodsPrice.getKillCount(), 60 * 60, TimeUnit.SECONDS);
                        //再次去执行lua脚本,扣减库存
                        stock = stock(killGoodCount, 1, STOCK_LUA);
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            } finally {
                if (timer != null) {
                    timer.cancel();
                }
                //释放锁 。自己加的锁不能让别人释放,自己只能释放自己的锁
                //这里要进行一个value值的比较,只要自己的value值相等才能释放
                redisLock.unlock();
            }
        }
        //如果是这种情况,秒杀成功
        boolean flag = stock >= 0;
        if (flag) {
            redisTemplate.opsForSet().add(KillConstants.KILLED_GOOD_USER + killId, userId);
        }
        return flag;
    }

lua脚本 STOCK_LUA:

 

 /**
     * 执行扣库存的脚本
     */
    public static final String STOCK_LUA;

    public static String STOCK_LUA_1 = "";

    public static String STOCK_LUA_INCR = "";



  static {
        /**
         *
         * @desc 扣减库存Lua脚本
         * 库存(stock)-1:表示不限库存
         * 库存(stock)0:表示没有库存
         * 库存(stock)大于0:表示剩余库存
         *
         * @params 库存key
         * @return
         * 		-3:库存未初始化
         * 		-2:库存不足
         * 		-1:不限库存
         * 		大于等于0:剩余库存(扣减之后剩余的库存)
         * 	    redis缓存的库存(value)是-1表示不限库存,直接返回1
         */
        StringBuilder sb = new StringBuilder();
        sb.append("if (redis.call('exists', KEYS[1]) == 1) then");//判断此商品是否存在
        sb.append("    local stock
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值