:racehorse:基于SpringBoot + MySQL + Redis + RabbitMQ + Guava开发的高并发商品限时秒杀系统

Overview

系统介绍

本系统是使用SpringBoot开发的高并发限时抢购秒杀系统,除了实现基本的登录、查看商品列表、秒杀、下单等功能,项目中还针对高并发情况实现了系统缓存、降级和限流。

开发工具

IntelliJ IDEA + Navicat + Sublime Text3 + Git + Chrome

压测工具

JMeter

开发技术

前端技术 :Bootstrap + jQuery + Thymeleaf

后端技术 :SpringBoot + MyBatis + MySQL

中间件技术 : Druid + Redis + RabbitMQ + Guava

秒杀优化方向

  1. 将请求尽量拦截在系统上游:传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小,我们可以通过限流、降级等措施来最大化减少对数据库的访问,从而保护系统。

  2. 充分利用缓存:秒杀商品是一个典型的读多写少的应用场景,充分利用缓存将大大提高并发量

实现技术点

1. 两次MD5加密

将用户输入的密码和固定Salt通过MD5加密生成第一次加密后的密码,再讲该密码和随机生成的Salt通过MD5进行第二次加密,最后将第二次加密后的密码和第一次的固定Salt存数据库

好处:

  1. 第一次作用:防止用户明文密码在网络进行传输
  2. 第二次作用:防止数据库被盗,避免通过MD5反推出密码,双重保险

2. session共享

验证用户账号密码都正确情况下,通过UUID生成唯一id作为token,再将token作为key、用户信息作为value模拟session存储到redis,同时将token存储到cookie,保存登录状态

好处: 在分布式集群情况下,服务器间需要同步,定时同步各个服务器的session信息,会因为延迟到导致session不一致,使用redis把session数据集中存储起来,解决session不一致问题。

3. JSR303自定义参数验证

使用JSR303自定义校验器,实现对用户账号、密码的验证,使得验证逻辑从业务代码中脱离出来。

4. 全局异常统一处理

通过拦截所有异常,对各种异常进行相应的处理,当遇到异常就逐层上抛,一直抛到最终由一个统一的、专门负责异常处理的地方处理,这有利于对异常的维护。

5. 页面缓存 + 对象缓存

  1. 页面缓存:通过在手动渲染得到的html页面缓存到redis
  2. 对象缓存:包括对用户信息、商品信息、订单信息和token等数据进行缓存,利用缓存来减少对数据库的访问,大大加快查询速度。

6. 页面静态化

对商品详情和订单详情进行页面静态化处理,页面是存在html,动态数据是通过接口从服务端获取,实现前后端分离,静态页面无需连接数据库打开速度较动态页面会有明显提高

7. 本地标记 + redis预处理 + RabbitMQ异步下单 + 客户端轮询

描述:通过三级缓冲保护,1、本地标记 2、redis预处理 3、RabbitMQ异步下单,最后才会访问数据库,这样做是为了最大力度减少对数据库的访问。

实现:

  1. 在秒杀阶段使用本地标记对用户秒杀过的商品做标记,若被标记过直接返回重复秒杀,未被标记才查询redis,通过本地标记来减少对redis的访问
  2. 抢购开始前,将商品和库存数据同步到redis中,所有的抢购操作都在redis中进行处理,通过Redis预减少库存减少数据库访问
  3. 为了保护系统不受高流量的冲击而导致系统崩溃的问题,使用RabbitMQ用异步队列处理下单,实际做了一层缓冲保护,做了一个窗口模型,窗口模型会实时的刷新用户秒杀的状态。
  4. client端用js轮询一个接口,用来获取处理状态

8. 解决超卖

描述:比如某商品的库存为1,此时用户1和用户2并发购买该商品,用户1提交订单后该商品的库存被修改为0,而此时用户2并不知道的情况下提交订单,该商品的库存再次被修改为-1,这就是超卖现象

实现:

  1. 对库存更新时,先对库存判断,只有当库存大于0才能更新库存
  2. 对用户id和商品id建立一个唯一索引,通过这种约束避免同一用户发同时两个请求秒杀到两件相同商品
  3. 实现乐观锁,给商品信息表增加一个version字段,为每一条数据加上版本。每次更新的时候version+1,并且更新时候带上版本号,当提交前版本号等于更新前版本号,说明此时没有被其他线程影响到,正常更新,如果冲突了则不会进行提交更新。当库存是足够的情况下发生乐观锁冲突就进行一定次数的重试。

9. 使用数学公式验证码

描述:点击秒杀前,先让用户输入数学公式验证码,验证正确才能进行秒杀。

好处:

  1. 防止恶意的机器人和爬虫
  2. 分散用户的请求

实现:

  1. 前端通过把商品id作为参数调用服务端创建验证码接口
  2. 服务端根据前端传过来的商品id和用户id生成验证码,并将商品id+用户id作为key,生成的验证码作为value存入redis,同时将生成的验证码输入图片写入imageIO让前端展示
  3. 将用户输入的验证码与根据商品id+用户id从redis查询到的验证码对比,相同就返回验证成功,进入秒杀;不同或从redis查询的验证码为空都返回验证失败,刷新验证码重试

10. 使用RateLimiter实现限流

描述:当我们去秒杀一些商品时,此时可能会因为访问量太大而导致系统崩溃,此时要使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:返回排队页面(高峰期访问太频繁,等一会重试)、错误页等。

实现:项目使用RateLimiter来实现限流,RateLimiter是guava提供的基于令牌桶算法的限流实现类,通过调整生成token的速率来限制用户频繁访问秒杀页面,从而达到防止超大流量冲垮系统。(令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务)

压测效果

优化前 :开启1000个线程循环10次同时访问,QPS = 423 优化前 优化后:QPS = 2501 优化后


本项目是学习了imooc网视频之后的个人理解和知识汇总,学习链接:https://coding.imooc.com/class/168.html

Comments
  • 乐观锁的version number在更新时没有传送

    乐观锁的version number在更新时没有传送

    两个问题 1、更新库存时没有送version number,所以更新库存失败 2、秒杀成功以后,返回的int 永远是1,作为订单号返回,是查不到订单的

    GoodsVo 增加 int version 和getter setter

    GoodsMapper getGoodsVoByGoodsId 增加返回 sg.version

    GoodsService reduceStock()方法增加 sg.setVersion(goods.getVersion());

    OrderService createOrder()方法 订单号要从实体类中取,否则update返回的永远是1 long orderId = orderInfo.getId();

    opened by baijy 2
  • redis password

    redis password

    redis pass 没有通过配置读取到实例化中,导致redis 设置了pass就会报错。 JedisPool jp = new JedisPool(config, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0); git中没有加redisConifg.getPassword() 在RedisPoolFactory.java中

    opened by jarqi 1
  • 点击立即秒杀 后台user对象内的属性值都为null

    点击立即秒杀 后台user对象内的属性值都为null

    点击立即秒杀 后台/do_seckill 方法中 user对象内的属性值都为null,当执行到//判断重复秒杀 SeckillOrder order = orderService.getOrderByUserIdGoodsId(user.getId(), goodsId);报空指针异常,请问为什么呢?

    opened by NJYts 1
  • 如果version失效了,更新失败 再次执行时没有取出表中最新的version,所以不管循环几次,更新也是失败的????

    如果version失效了,更新失败 再次执行时没有取出表中最新的version,所以不管循环几次,更新也是失败的????

    /** * 减少库存,每次减一 * * @return */ public boolean reduceStock(GoodsVo goods) { int numAttempts = 0; int ret = 0; SeckillGoods sg = new SeckillGoods(); sg.setGoodsId(goods.getId()); sg.setVersion(goods.getVersion()); do { numAttempts++; try { //TODO 如果version失效了,更新失败 再次执行时没有取出表中最新的version,所以不管循环几次,更新也是失败的???? ret = goodsMapper.reduceStockByVersion(sg); } catch (Exception e) { e.printStackTrace(); } if (ret != 0) break; } while (numAttempts < DEFAULT_MAX_RETRIES);

        return ret > 0;
    }
    
    opened by chen393804997 1
  • 乐观锁冲突重试时并没有更新版本号

    乐观锁冲突重试时并没有更新版本号

    do { numAttempts++; try { ret = goodsMapper.reduceStockByVersion(sg); } catch (Exception e) { e.printStackTrace(); } if (ret != 0) break; } while (numAttempts < DEFAULT_MAX_RETRIES);

    opened by roverbeta 1
  • 跑不了

    跑不了

    java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@117159c0: startup date [Wed Mar 23 20:50:25 CST 2022]; root of context hierarchy

    opened by BucolicLife 0
  • 预减库存如果库存小于0,为什么要调用afterPropertiesSet()更新每件商品?

    预减库存如果库存小于0,为什么要调用afterPropertiesSet()更新每件商品?

    秒杀下单时的一段代码: //预减库存 long stock = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10 if (stock < 0) { afterPropertiesSet(); long stock2 = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10 if(stock2 < 0){ localOverMap.put(goodsId, true); return Result.error(CodeMsg.SECKILL_OVER); } }

    下面这个是afterPropertiesSet方法定义,每次调用都要获取所有商品并且遍历商品,这样会消耗性能吧,而且,我只是下单某一件商品,为什么在遍历中将每件商品标记为false?原本某些标记为true的商品变为false了 public void afterPropertiesSet() { List<GoodsVo> goodsVoList = goodsService.listGoodsVo(); if (goodsVoList == null) { return; } for (GoodsVo goods : goodsVoList) { redisService.set(GoodsKey.getGoodsStock, "" + goods.getId(), goods.getStockCount()); //初始化商品都是没有处理过的 localOverMap.put(goods.getId(), false); } }

    opened by nicexiaoliang 1
  • 重复下单redis中库存还是会减小

    重复下单redis中库存还是会减小

    long stock = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
            if (stock < 0) {
                afterPropertiesSet();
                long stock2 = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
                if(stock2 < 0){
                    localOverMap.put(goodsId, true);
                    return Result.error(CodeMsg.SECKILL_OVER);
                }
            }
            //判断重复秒杀
            //如果重复秒杀,是否多次减库存?
            SeckillOrder order = orderService.getOrderByUserIdGoodsId(user.getId(), goodsId);
            if (order != null) {
                return Result.error(CodeMsg.REPEATE_SECKILL);
            }
    

    预减库存后,如果是重复秒杀,redis中的库存量还是会减少,导致其他用户不能下单

    opened by wangsenyan 4
Owner
FINN
FINN
hibernate redis 二级缓存使用 spring boot redis 配置

hibernate-redisson-spring-boot-starter 介绍 使用 Redisson 作为 hibernate redis 二级缓存提供器,redisson-hibernate-53(hibernate-version) 无法使用 spring boot 配置文件创建的 org

null 4 Jul 3, 2022
🦄 开源社区系统:基于 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ... 并提供详细的开发文档和配套教程。包含帖子、评论、私信、系统通知、点赞、关注、搜索、用户设置、数据统计等模块。

Echo — 开源社区系统 项目上线到服务器之后可能会出现各种各样的 BUG,比如 Elasticsearch 服务启动失败导致搜索模块不可用,但是在本地运行是完全没问题的,所以各位小伙伴可以放心下载部署。 ?? 项目简介 Echo 是一套前后端不分离的开源社区系统,基于目前主流 Java Web

小牛肉 434 Jan 7, 2023
backend for a sharing app using SpringBoot, Redis, MySQL, and AWS S3.

moments_v2_backend (Work In Progress) backend for a sharing app using SpringBoot, Redis, MySQL, and AWS S3. This is the second version of my project S

Haiming Sun 53 Dec 26, 2022
lilishop是采用JAVA开发的B2B2C多用户商城系统/电商系统/电子商务。基于当前流行技术组合的前后端分离商城系统:后端使用 SpringBoot、MybatisPlus、SpringSecurity、redis、ES、mysql、mongodb等主流技术,前端使用vue框架iview、uniapp。支持分布式部署,分布式事务,支持docker、k8s。商城支持 PC、WAP、H5、小程序、APP等各个客户端

Lilishop B2B2C商城系统 官方公众号 & 开源不易,如有帮助请点Star 介绍 官网:https://pickmall.cn Lilishop 是一款Java开发,基于SpringBoot研发的B2B2C多用户商城,前端使用 Vue、uniapp开发 系统全端全部代码开源 产品前后端分离

null 3k Dec 31, 2022
mall4cloud微服务商城,基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的B2B2C微服务商城系统,采用主流的互联网技术架构、全新的UI设计 B2B2C微服务商城|小程序微服务商城|

README 前言 本商城是基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的微服务B2B2C电商商城系统,采用主流的互联网技术架构、全新的UI设计、支持集群部署、服务注册和发现以及拥有完整的订单流程等

null 3k Jan 1, 2023
消息推送平台 - 所使用的技术栈包括:SpringBoot、SpringDataJPA、MySQL、Docker、docker-compose、Kafka、Redis、Apollo、prometheus、Grafana、GrayLog等等

项目介绍 austin项目核心功能:发送消息 项目出现意义:只要公司内有发送消息的需求,都应该要有类似austin的项目,对各类消息进行统一发送处理。这有利于对功能的收拢,以及提高业务需求开发的效率 系统项目架构 austin项目核心流程:austin-api接收到发送消息请求,直接将请求进MQ。a

Java3y 2.9k Dec 31, 2022
A high availability shopping(ecommerce) system using SpringBoot, Spring Cloud, Eureka Server, Spring Cloud Gateway, resillience4j, Kafka, Redis and MySQL.

High-availability-shopping-system A high availability shopping(ecommerce) system using SpringBoot, Spring Cloud, Eureka Server, Spring Cloud Gateway,

LeiH 1 Oct 26, 2022
A personal blog based on Vue+SpringBoot+MySql+Redis+Shiro+JWT

项目:Vue-SpringBoot-PersonalBlog 个人博客网址:http://www.huchao.vip/blogs CSDN:毛_三月 介绍 一个基于SpringBoot + Vue+MybatisPlus+Shiro+JWT+Redis开发的前后端分离博客项目,带有超级详细开发文档

Chao. Hu 26 Dec 20, 2022
:herb: 基于springboot的快速学习示例,整合自己遇到的开源框架,如:rabbitmq(延迟队列)、Kafka、jpa、redies、oauth2、swagger、jsp、docker、spring-batch、异常处理、日志输出、多模块开发、多环境打包、缓存cache、爬虫、jwt、GraphQL、dubbo、zookeeper和Async等等:pushpin:

欢迎大家留言和PR~ Tip: 技术更新换代太快,本仓库仅做参考,自己的项目具体使用哪个版本还需谨慎思考~(不推荐使用最新的版本,推荐使用(最新-1|2)的版本,会比较稳定) spring-boot-quick 前言   自己很早就想搞一个总的仓库就是将自己平时遇到的和学习到的东西整合在一起,方便后

wangxc 2.1k Jan 2, 2023
A lightweight messaging library that simplifies the development and usage of RabbitMQ with the AMQP protocol.

kryo-messaging This library contains a simple MessagingService which simplifies the setup and work with RabbitMQ and the AMQP protocol. Usage Gradle r

Kryonite Labs 3 Jan 10, 2022
Mock for RabbitMQ Java amqp-client

RabbitMQ-mock Mock for RabbitMQ Java amqp-client. Compatible with versions 4.0.0 to 5.9.0 of com.rabbitmq:amqp-client Compatible with versions 3.6.3 t

Fridujo 160 Dec 28, 2022
mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现,采用Docker容器化部署。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。

mall 友情提示 快速体验项目:在线访问地址。 全套学习教程:《mall学习教程》。 微服务版本:基于Spring Cloud Hoxton & Alibaba的项目:mall-swarm。 专属学习路线:学习不走弯路,整理了套非常不错的《mall专属学习路线》。 项目交流:想要加群交流项目的朋友

macro 62.9k Jan 4, 2023
【咕泡学院实战项目】-基于SpringBoot+Dubbo构建的电商平台-微服务架构、商城、电商、微服务、高并发、kafka、Elasticsearch

咕泡商城- 微服务架构实战 咕泡商城是咕泡学院 Java架构课程中,帮助学员对于技术更好落地的一个实战项目,项目基于springboot2.1.6.RELEASE+Dubbo2.7.3 来构建微服务。 业务模块划分,尽量贴合互联网公司的架构体系。所以,除了业务本身的复杂度不是很高之外,整体的架构基本

Mic 4.5k Dec 26, 2022
一个小而美的低代码全栈开发平台,一键生成后端api接口+前端页面代码+在线接口文档,节省50%的前后端开发的工作量。基于springboot +mybatis+spring security+vue 技术栈

YOMA [toc] 一个小而美的低代码全栈开发平台,一键生成后端api接口+前端页面代码+在线接口文档,节省50%的前后端开发的工作量,平台代码100%开源。平台适用于企业信息化、政务、中小型互联网等项目 平台采用前后端分离架构,基于如下流行的开源框架,易上手+便于后期维护 后端:https://

Shihao Ma 149 Dec 9, 2022
基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统

平台简介 芋道,一套全部开源的企业级的快速开发平台,毫无保留给个人及企业免费使用。 有任何问题,或者想要的功能,可以在 Issues 中提给艿艿。 前端采用 vue-element-admin。 后端采用 Spring Boot、MySQL、Redis。 权限认证使用 Spring Security

芋道源码 8.2k Jan 1, 2023
😎基于SpringBoot+MyBatis进行前后端开发的个人博客网站,优化了许多功能模块,版本不断维护中。(期末项目/毕业设计/新手推荐)

Spring-Blog 框架:Springboot 数据库持久层:Mybatis 文章评论插件:Valine 分页插件:PageHelper 后台UI框架:X-admin框架,即LayUI框架 数据库连接池:hikari 数据库:MySQL 日志:Log4J 后台配置: properties 缓存实

coder_ryan 142 Jan 5, 2023
w-blog后端部分,基于springboot、mybatis、mysq···的一个简单博客后端程序

w-blog 描述 一个后端使用Spring Boot 2.x、前台使用nuxtJs、后台使用vue的个人博客 简介 链接 Gitee链接: api: https://gitee.com/windsnowli/w-blog-api 前台: https://gitee.com/windsnowli/v

null 13 Jun 1, 2022
Spring-boot application using redis as a caching database

Java Spring-boot application using Redis as a caching database Running Application Entities involved Two main entities are involved MasterHouse (maste

null 18 Aug 9, 2022
Spring Data Redis extensions for better search, documents models, and more

Object Mapping (and more) for Redis! Redis OM Spring extends Spring Data Redis to take full advantage of the power of Redis. Project Stage Snapshot Is

Redis 303 Dec 29, 2022