实战深入了解redis+消息队列如何实现秒杀

Posted 陈ོ努力

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实战深入了解redis+消息队列如何实现秒杀相关的知识,希望对你有一定的参考价值。

SpringBoot +Redis +RabbitMQ 实现高并发限时秒杀

所谓秒杀,从业务角度看,是短时间内多个用户“争抢”资源,这里的资源在大部分秒杀场景里是商品;将业务抽象,技术角度看,秒杀就是多个线程对资源进行操作,所以实现秒杀,就必须控制线程对资源的争抢,既要保证高效并发,也要保证操作的正确。

开发环境

SpringBoot+mysql+maven+JDK8+RabbitMQ+Redis

测试环境

Jmeter测试工具

环境安装

安装RabbitMQ

docker安装:https://blog.csdn.net/qq_33612228/article/details/103732890
windows安装:https://blog.csdn.net/m0_37034294/article/details/82839494

需要安装mq的可视化工具

安装Redis

docker安装:https://blog.csdn.net/qq_33612228/article/details/10360918
windows安装:https://blog.csdn.net/qq_39135287/article/details/82686837
springboot整合redis:https://blog.csdn.net/qq_33612228/article/details/103700543

redis需要安装客户端,方便数据查看。

安装 Jmeter测试工具

windows安装:https://blog.csdn.net/liuyanh2006/article/details/82494548
mac安装: https://blog.csdn.net/zgy_boke/article/details/102689531
mac安装需要的Homebrew:https://blog.csdn.net/o_o814222198/article/details/120185851?spm=1001.2014.3001.5502

maven安装

mac安装:https://www.jianshu.com/p/191685a33786
window安装:https://www.cnblogs.com/eagle6688/p/7838224.html

代码模块

数据库设计

商品库存表:stock
CREATE TABLE `stock` (
  `id` varchar(64) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `stock` varchar(255) DEFAULT NULL,
  `remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  `update_date` datetime DEFAULT NULL COMMENT '最后更新时间',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) NOT NULL DEFAULT '',
  `create_by` varchar(64) NOT NULL DEFAULT '',
  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='商品库存表';
秒杀订单表:t_order
CREATE TABLE `t_order` (
  `id` varchar(64) NOT NULL,
  `order_name` varchar(255) DEFAULT NULL,
  `order_user` varchar(255) DEFAULT NULL,
  `remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  `update_date` datetime DEFAULT NULL COMMENT '最后更新时间',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) NOT NULL DEFAULT '',
  `create_by` varchar(64) NOT NULL DEFAULT '',
  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='秒杀订单表';

pom.xml

  1. 集成MybatisPlus
  2. fastjosn依赖
  3. Druid
  4. MyBatis增强工具
  5. lombok
  6. websocket
  7. 前端页面通过thymeleaf渲染
  8. 工具类包
  9. swagger-ui
  10. redis
  11. RabbitMQ
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>集成MybatisPlus</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <druid.version>1.1.14</druid.version>
        <mysql-connector-java.version>8.0.11</mysql-connector-java.version>
        <mybatis-plus-boot-starter.version>3.1.1</mybatis-plus-boot-starter.version>
        <mybatis-plus-generator.version>3.1.1</mybatis-plus-generator.version>
        <lombok.version>1.18.0</lombok.version>
        <swagger.version>2.9.2</swagger.version>
        <fastjson.version>1.2.47</fastjson.version>
        <commons-text.version>1.6</commons-text.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--依赖管理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.8.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector-java.version}</version>
        </dependency>
        <!--阿里巴巴fastjosn依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- Druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- MyBatis增强工具-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator.version}</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <!-- 前端页面通过thymeleaf渲染 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--工具类包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>${commons-text.version}</version>
        </dependency>

        <!--swagger-ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

  1. 数据源
  2. 端口号
  3. redis
  4. mq
  5. 扫描mapper文件
  6. 文件上传
  7. 页面模板
  8. log日志配置
spring:
  datasource:
    url: jdbc:mysql://182.92.210.212:3306/order_db?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: ***
    # 使用Druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
  data:
    redis:
      repositories:
        enabled: false
  redis:
    database: 0   # redis数据库索引(默认为0),我们使用索引为其他(0-15)的数据库,避免和其他数据库冲突
    host: 182.92.210.212
    port: 6379
    password: ***
  rabbitmq:  #mq配置
    host: localhost
    port: 5672
    username: guest
    password: guest
  resources:
    static-locations: classpath:/static #这个配置项告诉springboot去哪找资源 最关键的地方
  profiles:
    active: dev
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB #允许上传的文件大小
      max-request-size: 10MB
  thymeleaf:
    cache: false
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML5
    context-type: text/html
  mvc:
    view:
      prefix: /
      suffix: .html
    static-path-pattern: /**  #这个配置告诉springboot,应该以什么方式去找资源,默认是/*
mybatis-plus:
  mapper-locations: classpath*:com.example.demo.mapper/*.xml
  global-config:
    db-config:
      id-type: uuid
      field-strategy: not_null
    refresh: true
  configuration:
    map-underscore-to-camel-case: false
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

server:
  port: 8090
logging:
  config: classpath:logback-spring.xml

logback-spring.xml

  1. 各级别日志分开打印,一个等级一个日志文件。
  2. 每天存档。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback</contextName>
    <property name="path" value="./log"/>
    <property name="maxHistory" value="30"/>
    <property name="maxFileSize" value="50MB"/>
    <!--输出sql语句-->
    <logger name="com.example.demo.mapper" level="debug"/>
    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
             <level>ERROR</level>
         </filter>-->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--debug_file-->
    <appender name="debug_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一归档 -->
            <fileNamePattern>${path}/logback_debug.log.%d{yyyy-

以上是关于实战深入了解redis+消息队列如何实现秒杀的主要内容,如果未能解决你的问题,请参考以下文章

Redis基于(ListPubSubStream消费者组)实现消息队列,基于Stream结构实现异步秒杀下单

Redis之消息队列实现

Redis之秒杀下单优化以及认识redis消息队列

Redis之秒杀下单优化以及认识redis消息队列

深入剖析Redis高可用系列:持久化 AOF和RDB

Redis个人笔记:Redis应用场景,Redis常见命令,Reids缓存击穿穿透,Redis分布式锁实现方案,秒杀设计思路,Redis消息队列,Reids持久化,Redis主从哨兵分片集群