likes
comments
collection
share

《黑马点评》异步秒杀优化|消息队列

作者站长头像
站长
· 阅读数 60

异步秒杀优化

异步:

比如你早上起床后边吃早饭边看电视(异步),而不是吃完饭再看电视(同步)。很好理解吧,反着来就行。

参考链接

blog.csdn.net/bulubulua/a…

Web server failed to start. Port 8082 was already in use.解决办法,今早上没搞定现在看这篇文章搞定了。

blog.csdn.net/Htupc/artic…

《黑马点评》异步秒杀优化|消息队列

写完了,给我头都绕晕了,在这做一个总结:

秒杀业务的优化思路是什么?

先利用Redis完成库存余量、一人一单判断,完成抢单业务 再将下单业务放入阻塞队列,利用独立线程异步下单

基于阻塞队列的异步秒杀存在哪些问题?

内存限制问题 数据安全问题

任何人来调用创建订单逻辑,进入到seckillVoucher

先去调用lua脚本,尝试判断用户有没有购买资格,然后进行一系列操作

 @Override
    public Result seckillVoucher(Long voucherId) {
        //获取用户
        Long userId = UserHolder.getUser().getId();
//        long orderId = redisIdWorker.nextId("order");
        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),//空集合
                voucherId.toString(), userId.toString()//传入两个参数
        );
        int r = result.intValue();//转型
        //2.判断结果是否为0
        if (r != 0) {
            // 2.1.不为0 ,代表没有购买资格
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        //这里代表有购买资格 把下单信息保存导阻塞队列
        //todo:保存到阻塞队列
        VoucherOrder voucherOrder = new VoucherOrder();
        // 2.3.订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        // 2.4.用户id
        voucherOrder.setUserId(userId);
        // 2.5.代金券id
        voucherOrder.setVoucherId(voucherId);
        // 2.6.放入阻塞队列
        orderTasks.add(voucherOrder);
        //3.获取代理对象
        //获取代理对象 事务
        proxy = (IVoucherOrderService) AopContext.currentProxy();

        //3.返回订单id
        return Result.ok(orderId);
    }

放入阻塞队列之后要去执行义务下单,这个任务认为百分百成功,因为库存是足的,怎么操作?

1.初始化时执行线程池,线程池业务就是从这个组合队列不断的取出这个订单,去创建。

// 用于线程池处理的任务
    // 当初始化完毕后,就会去从对列中去拿信息
    private class VoucherOrderHandler implements Runnable {
        @Override
        public void run() {
            while (true) {

                try {
                    //1. 获取队列中的订单信息
                    VoucherOrder voucherOrder = orderTasks.take();

                    //2. 创建订单
                    handleVoucherOrder(voucherOrder);//定义函数
                } catch (InterruptedException e) {
                    log.error("处理订单异常", e);//这里可能有异常 上边加上日志
                }
            }
        }
    }

2.创建的流程:获取锁什么的,锁拿到去创建订单

/*
    方法前面void的不再返回给前端仍任何东西了 因为已经做异步处理了
     */
    private void handleVoucherOrder(VoucherOrder voucherOrder) {
        //1.获取用户id,不能从ThreadLocal中取,因为是从线程池获取新线程
        Long userId = voucherOrder.getUserId();
        //2.创建锁对象
        RLock lock = redissonClient.getLock("lock:order:" + userId);
        //获取锁
        boolean isLock = lock.tryLock();
        if (!isLock) {
            //获取锁失败 返回错误或重试
            log.error("不允许重复下单");
            return;
        }
        try {
            //获取代理对象 (事务)
            //作为一个子线程没办法从ThreadLocal中取出
            proxy.createVoucherOrder(voucherOrder);
        } finally {
            //释放锁
            lock.unlock();
        }
    }

3.创建订单:一人一单进来,做一个判断是否存在,做一个库存判断 ,创建订单

 @Transactional
    public void createVoucherOrder(VoucherOrder voucherOrder) {

        // 5.一人一单逻辑
//        Long userId = UserHolder.getUser().getId();
        Long userId = voucherOrder.getUserId();
        // 5.1.查询订单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
        // 5.2判断是否存在
        if (count > 0) {
            //用户已经购买过了
            log.error("用户已经购买过一次了");//和前面一样不太可能返回了直接写个日志就行
            return;
        }
        // 6.扣减库存
//        boolean success = seckillVoucherService.update()
//                .setSql("stock= stock -1") //set stock = stock -1
//                .eq("voucher_id", voucherId).eq("stock",voucher.getStock()).update(); //where id = ? and stock = ?
        boolean success = seckillVoucherService.update()
                .setSql("stock= stock -1")
                .eq("voucher_id", voucherOrder.getVoucherId()).gt("stock"0).update(); //where id = ? and stock > 0
        if (!success) {
            //扣减库存
            log.error("库存不足!");
            return;
        }
        // 8.保存订单
        save(voucherOrder);
//        return Result.ok(orderId);
    }

消息队列

消息队列:

就是我们常说的MQ,英文叫Message Queue,是作为一个单独的中间件产品存在的,独立部署。

《黑马点评》异步秒杀优化|消息队列

参考链接

blog.csdn.net/weixin_5129…

zhuanlan.zhihu.com/p/431149128

《黑马点评》异步秒杀优化|消息队列

《黑马点评》异步秒杀优化|消息队列

基于Stream的消息队列

《黑马点评》异步秒杀优化|消息队列

Redis消息队列 基于Stream消息队列实现异步秒杀

《黑马点评》异步秒杀优化|消息队列

《黑马点评》异步秒杀优化|消息队列

下面的我听不懂了,就自己跟着写了一下脚本改了一下代码太绕了,等我有时间又回来重新学吧。

转载自:https://juejin.cn/post/7370841518791475235
评论
请登录