12.1_黑马Redis实战篇Redis优化秒杀Redis消息队列实现异步秒杀

目录

实战篇22

实战篇23

实战篇24

实战篇25

实战篇26

实战篇27

实战篇28

实战篇29

实战篇30


实战篇22

将任务分布给不同的线程去做,可以加快程序运行速度。

放到lua脚本,保证原子性。同时,这样的优化,可以减轻数据库的压力。 

  

实战篇23

thinking:sismember?

thinking:intValue?

intValue的用法_一般社员的博客-CSDN博客

实战篇24

thinking:阻塞队列?

BlockingQueue:当一个线程尝试在队列里面获取元素时,如果没有元素,线程就会被阻塞,直到队列中有元素,他才会被唤醒并且获取元素

thinking:如何翻译文档或者单词?

IDEA翻译插件Translation配置翻译引擎解决翻译错误的问题_飞牛鱼鱼的博客-CSDN博客

如果要翻译文档,出现这样的效果

则鼠标移到文档里面,然后右键,translate documentation

实战篇25

实战篇26

实战篇27

实战篇28

实战篇29 

不会漏消息

 

 

实战篇30

thinking:toString()、String.valueOf()、(String) 强转的区别?

toString()、String.valueOf()、(String) 强转的区别_string.valueof和tostring的区别-CSDN博客

String.valueof()与toString()方法的区别_stringvalueof和tostring_想起飞的张张的博客-CSDN博客

 thinking:beanutil.fillbeanwithmap?

hutool工具包快速入门_beanutil.fillbeanwithmap-CSDN博客

package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedissonClient redissonClient;

    //提前读好这个文件,避免多次读取
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    //因为是静态的,因此在静态代码块里面搞
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    //private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 *1024);
    //创建线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();
    //线程任务 这个要在初始化的时候就要完成了。

    @PostConstruct
    private void init() {
        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
    }

    private class VoucherOrderHandler implements Runnable {
        String queueName = "stream.orders";

        @Override
        public void run() {
            while (true) {
                try {
                    //1. 获取消息中的订单信息
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create(queueName, ReadOffset.lastConsumed())
                    );
                    //2. 判断消息获取是否成功
                    if (list == null || list.isEmpty()) {
                        //2.1 如果获取失败,说明pending-list没有异常消息,结束循环
                        break;
                    }
                    //3.解析消息中的订单消息
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> values = record.getValue();
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
                    //4.如果获取成功,可以下单
                    handleVoucherOrder(voucherOrder);
                    //5.ACK确认
                    stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
                } catch (Exception e) {
                    log.error("处理pending-list订单异常", e);
                    handlePendingList();
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }

        private void handlePendingList() {
            while (true) {
                try {
                    //1. 获取pending-list中的订单信息
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(queueName, ReadOffset.from("0"))
                    );
                    //2. 判断消息获取是否成功
                    if (list == null || list.isEmpty()) {
                        //2.1 如果获取失败,说明没有消息,继续下一次循环
                        continue;
                    }
                    //3.解析消息中的订单消息
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> values = record.getValue();
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
                    //4.如果获取成功,可以下单
                    handleVoucherOrder(voucherOrder);
                    //5.ACK确认
                    stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
                } catch (Exception e) {
                    log.error("处理订单异常", e);
                }
            }
        }
    }

    private void handleVoucherOrder(VoucherOrder voucherOrder) {
        //获取用户
        Long userId = voucherOrder.getUserId();
        //创建锁对象
        RLock lock = redissonClient.getLock("lock:order:" + userId);
        //获取锁
        //不传参数,代表我失败了立即返回
        boolean isLock = lock.tryLock();
        //判断是否获取锁成功
        if (!isLock) {
            //获取锁失败,返回错误或重试
            log.error("不允许重复下单");
            return;
        }
        try {
            proxy.createVoucherOrder(voucherOrder);
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    private IVoucherOrderService proxy;

    @Override
    public Result seckillVoucher(Long voucherId) {
        //获取用户
        Long userId = UserHolder.getUser().getId();
        //获取订单id
        long orderId = redisIdWorker.nextId("order");

        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString(), String.valueOf(orderId)
        );
        //2.判断结果是为0
        int r = result.intValue();
        if (r != 0) {
            //2.1 不为0.代表没有购买资格
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        //3 获取代理对象
        proxy = (IVoucherOrderService) AopContext.currentProxy();
        //4. 返回订单id
        return Result.ok(orderId);
    }

    //@Override
//    public Result seckillVoucher(Long voucherId) {
//        //获取用户
//        Long userId = UserHolder.getUser().getId();
//        //1.执行lua脚本
//        Long result = stringRedisTemplate.execute(
//                SECKILL_SCRIPT,
//                Collections.emptyList(),
//                voucherId.toString(), userId.toString()
//        );
//        //2.判断结果是为0
//        int r = result.intValue();
//        if(r != 0){
//            //2.1 不为0.代表没有购买资格
//            return Result.fail(r ==1 ? "库存不足" : "不能重复下单");
//        }
//        //2.2 为0,有购买资格,把下单信息保存到阻塞队列
//        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();
//        //4. 返回订单id
//        return Result.ok(orderId);
//    }
    @Transactional
    public void createVoucherOrder(VoucherOrder voucherOrder) {
        //5,一人一单
        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")
                .eq("voucher_id", voucherOrder.getVoucherId())
                .gt("stock", 0)
                .update();
        if (!success) {
            //扣除失败
            log.error("库存不足!");
            return;
        }
        //7,创建订单
        save(voucherOrder);
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/207276.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《尚品甄选》:后台系统——商品管理,对商品数据进行维护(debug一遍)

文章目录 一、表结构介绍二、列表查询三、添加功能(复杂)3.1 加载品牌数据3.2 加载商品单元数据3.3 加载商品规格数据3.4 保存商品数据 四、修改功能4.1 查询商品详情4.2 保存修改数据 五、删除商品六、商品审核七、商品上下架 一、表结构介绍 商品管理就是对电商项目中所涉及…

基于H5“汉函谷关起点新安县旅游信息系统”设计与实现

目 录 摘 要 1 ABSTRACT 2 第1章 绪论 3 1.1 系统开发背景及意义 3 1.2 系统开发的目标 3 第2章 主要开发技术介绍 5 2.1 H5技术介绍 5 2.2 Visual Studio 技术介绍 5 2.3 SQL Server数据库技术介绍 6 第3章 系统分析与设计 7 3.1 可行性分析 7 3.1.1 技术可行性 7 3.1.2 操作…

办公软件PDF转换工具 - Bruce的PDF工具pdftool

Bruce的PDF工具 - 办公软件PDF转换工具 - pdftool&#xff0c;支持&#xff1a; 1、图片转PDF&#xff0c;支持图片自动压缩&#xff0c;可预览图片 2、合并PDF&#xff0c;支持多个PDF合并成一个PDF 3、PDF转图片&#xff0c;PDF的每页转成一张图片 4、OFD转PDF&#xff0c;O…

Postman:专业API测试工具,提升Mac用户体验

如果你是一名开发人员或测试工程师&#xff0c;那么你一定知道Postman。这是一个广泛使用的API测试工具&#xff0c;适用于Windows、Mac和Linux系统。今天&#xff0c;我们要重点介绍Postman的Mac版本&#xff0c;以及为什么它是你进行API测试的理想选择。 一、强大的功能和易…

honle电源维修UV电源控制器EVG EPS40C-HMI

好乐UV电源控制器维修&#xff1b;honle控制器维修&#xff1b;UV电源维修MUC-Steuermodul 2 LΛmpen D-82166 主要维修型号&#xff1a; EVG EPS 60/120、EVG EPS 100、EVG EPS200、EVG EPS 220、EVG EPS 340、EVG EPS40C-HMI、EVG EPS60 HONLE好乐uv电源维修故障包括&#…

2023年第十二届数学建模国际赛小美赛B题工业表面缺陷检测求解分析

2023年第十二届数学建模国际赛小美赛 B题 工业表面缺陷检测 原题再现&#xff1a; 金属或塑料制品的表面缺陷不仅影响产品的外观&#xff0c;还可能对产品的性能或耐久性造成严重损害。自动表面异常检测已经成为一个有趣而有前景的研究领域&#xff0c;对视觉检测的应用领域有…

智能优化算法应用:基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.平衡优化器算法4.实验参数设定5.算法结果…

Docker容器中的OpenCV:轻松构建可移植的计算机视觉环境

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 构建可移植的计算机视觉环境 文章目录 前言引言简介&#xff1a;目的和重要性&#xff1a; 深入理解Docker和OpenCVDocker的基本概念和优势&#xff1a;OpenCV简介和应用领域&#xff1a;…

几个linux指令提升编程效率

history history命令是Linux/Unix系统中的一个常用命令&#xff0c;用于查看当前用户在命令行中执行过的命令历史记录。该命令允许用户查看、搜索、编辑和执行之前执行过的命令&#xff0c;为用户提供了方便、快捷的操作方式。 查看历史命令&#xff1a; history查看最近n条…

nginx三种虚拟主机的配置(IP,端口,域名)

准备工作&#xff1a; [rootbogon ~]# mkdir -p /data/nginx{1..3} #-p是用于递归创建使用 [rootbogon ~]# echo "hello nginx1" > /data/nginx1/index.html [rootbogon ~]# echo "hello nginx2" > /data/nginx2/index.html [rootbogon ~]# echo &q…

adb环境搭建(adb下载与安装)

文章目录 前言一、adb下载二、adb安装1.将下载的安装包解压缩2.将解压缩后的文件夹放到自己想存放的目录下&#xff08;不要放到带有中文的目录下&#xff09;——我这放到D盘根目录下3.配置环境变量3.1.鼠标放到 "此电脑"→鼠标右击→选择属性3.2.点击 "高级系…

海银・颖奕海南国际健康管理基地启航!“财富+健康”双轮驱动战略加速中

现场&#xff0c;颖奕集团、颖奕生物科技集团董事长凌临贵&#xff0c;海南博鳌乐城国际医疗旅游先行区管理局党委书记、局长贾宁&#xff0c;海银控股董事长韩宏伟&#xff08;从左至右&#xff09;共同启动该项目。 11月24日&#xff0c;“海银颖奕海南国际健康管理基地”在…

正则表达式及文本三剑客grep sed awk

正则表达式 1.元字符 . //匹配任意单个字符&#xff0c;可以是个汉字 [yang] //匹配范围内的任意单个字符 [^y] //匹配处理指定范围外的任意单个字符 [:alnum:] //字母和数字 [:alpha:] //代表…

二叉树的操作(C++实现)

目录 ⚽实现要求&#xff1a; &#x1f3d0;题目分析&#xff1a; &#x1f3c0;代码展示&#xff1a; &#x1f4cc;前提类和函数声明&#xff1a; &#x1f94e;模块一&#xff08;层次—>创建二叉树&#xff09;&#xff1a; &#x1f3b1;模块二&#xff08;三种…

QT Creator 保存(Ctrl+S)时,会将Tab制表符转换为空格

今天在写makefile文件时&#xff0c;发现QT Creator 保存(CtrlS)时&#xff0c;会将Tab制表符转换为空格&#xff0c;之前没有发现&#xff0c;略坑&#xff0c;官网上也有说明&#xff0c;点这里 简单来说&#xff0c;解决办法如下 依次点击&#xff1a;Tools ->Options-&g…

C51--DHT11数据读取

DHT11传输0的时序分析&#xff1a; DHT11传输1的时序分析&#xff1a; 用while(dht)卡点&#xff0c;当不满足while时&#xff0c;信号拉低&#xff1b; 用while(&#xff01;dht)卡点&#xff0c;当不满足while时&#xff0c;信号拉高。 传输0和1时有效数据都是高电平&…

每日一题:LeetCode-1089. 复写零

每日一题系列&#xff08;day 09&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

Linux:服务器管理工具宝塔(bt)安装教程

一、简介 bt宝塔Linux面板是提升运维效率的服务器管理软件&#xff0c;支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等多项服务的管理功能 二、安装 使用 SSH 连接工具&#xff0c;如堡塔SSH终端连接到您的 Linux 服务器后&#xff0c;挂载磁盘&#xff0c;根据系统执…

微信如何单独隐藏某个人的聊天记录?

微信&#xff0c;如今已成为我们生活中不可或缺的沟通工具&#xff0c;它的应用范围涵盖了工作、学习及日常生活的方方面面。然而&#xff0c;有时为了保护个人隐私&#xff0c;或是不愿让他人看到特定对话&#xff0c;我们需要对与某人的聊天记录进行隐藏。那么&#xff0c;微…

【已解决】如何打开设置了密码的7Z压缩文件?

7Z是一种常见的压缩文件格式&#xff0c;相比RAR和ZIP格式&#xff0c;它的压缩率更高&#xff0c;可以压缩出更小的文件体积&#xff0c;也同样可以设置密码保护&#xff0c;那设置了密码的7Z压缩文件要如何打开呢&#xff1f; 我们知道&#xff0c;7Z压缩文件设置密码保护后…