Java 商品抢购系统设计,实现与优化

参考教程:https://www.bilibili.com/video/BV1sf4y1L7KE

0. 使用技术

Java 1.8
SpringBoot 2.7.4
RabbitMQ 3.11
Redis 6.2.7
Mybatis-plus 3.5.2
MySQL 5.7

1. 流程

  • 功能开发
  • 系统压测
  • 分布式会话
  • 优化
    • 页面优化
    • 服务优化
    • 安全优化

2. 高并发带来的一些问题

可能会遇到黄牛,大量并发写和并发读。

因此设计系统要保证高可用保证数据一致性高性能

3. 参考配置

Druid 数据源配置属性说明

MybatisX逆向工程

Mybatis 执行器

4. 分布式 Session 问题

多台 Tomcat 配合 Nginx 出现用户登录的问题。

Nginx 使用默认的负载均衡策略(轮询),请求将会按照时间顺序逐一分发到后端应用上。

比如,首先用户在 Tomcat1 上登陆后,用户信息保存在 Tomcat1 的 Session 中。过了一会请求被 Nginx 分发到了 Tomcat2 上,因为 Tomcat2 上的 Session 没有用户信息,此时又要登录。

4.1 可使用的解决方案

4.1.1 Session复制

优点:

  • 无需修改代码,只需要修改 Tomcat 配置

缺点:

  • Session 同步传输占用内网贷款
  • 多台 Tomcat 同步性能指数下降
  • Session 占用内存,无法有效水平扩展
    • 我加机器就是为了扩展内存,你这 Session 同步,每台机器还是占用那么多内存,太冗余了

4.1.2 前端存储

优点:

  • 不占服务器内存

缺点:

  • 存在安全风险

    • Cookie 是明文的,不安全
  • 数据大小受到 Cookie 限制
  • 占用外网带宽
    • 前端到后端传送对象也需要占用带宽的

4.1.3 Session 粘滞

优点:

  • 无需修改代码
  • 服务端可以水平扩展

缺点:

  • 增加新机器,会重新 Hash,导致重新登陆
  • 应用重启,需要重新登陆

4.1.4 后端集中存储

优点:

  • 安全
  • 容易水平扩展

缺点:

  • 增加复杂度
  • 需要修改代码

4.2 选择第四种方案(Redis)

docker 安装 Redis 6.2.7

客户端使用 Another Redis Desktop Manager 1.5.8

4.2.1 示例

set key value [ex 10 | px 10000] [nx | xx]
  • nx 表示当不存在该 key 设置成功
  • xx 表示存在该 key 设置成功
  • ex 10 十秒后过期
  • px 10000 一万毫秒(即十秒)后过期

4.3 SpringSession 实现方式(抛弃这种实现)

当执行 session.setAttribute() 时会自动导入 Redis

4.4 直接用 Redis 存储信息的实现方式(使用该实现)

首先实现序列化方法,默认的 JDK 序列方法可读性有点差。采用 Jackson 的序列化。

首次登录先存 Cookie,然后在 Redis 中存储 {“user:cookie”:userObject},下一次访问的时候直接拿着本地的 Cookie 去 Redis 取信息。

如果每个页面都需要判断是否登录太麻烦了。如何解决?

  • 先配置用户参数解析器,在解析器中通过 cookie 向 Redis 中取信息,然后直接将对象变为控制器的入参即可。

5. 优化前 Jmeter 压测记录

5.0 测试环境

  1. Windows 10,Intel i7 7700HQ,内存 16G
  2. Linux CentOS 7(虚拟机环境)4 核心,内存 4G

redis 单节点,mysql

5.1 获取商品列表信息

设置线程数 5000,每个线程重复请求 10 次。

重复此操作 3 次。

5.1.1 Windows 测试

QPS:1498.9

5.1.2 Linux 测试

QPS:832.8

5.2 秒杀

设置商品为 10。

设置线程数 1000,每个线程重复请求 10 次。

重复此操作 3 次。

5.2.1 Windows 测试

QPS:1111.9

可以看到数据库中超卖了 2 件:

订单表和秒杀订单表中的数据条数各有 338 条:

5.2.2 Linux 测试

QPS:812.1

数据中超卖了 18 件:

订单表和秒杀订单表中的数据条数各有 196 条数据:

5.3 总结

可以看到在并发环境下会出现超卖问题,以及商品库存小于 1 的时候还会加入订单。接下来将基于此进行优化,解决线程同步以及提高 QPS。

6. 初次优化

6.1 对象 JSON 缓存

每次获取商品列表或者某件商品的信息的时候,先从 Redis 中取,若 Redis 中没有,再去数据库找,并加入 Redis 缓存中。

// 先从 Redis 中取缓存, 若返回的对象不为空则直接返回对象
ValueOperations valueOperations = redisTemplate.opsForValue();
List<GoodsVo> goodList = (List<GoodsVo>) valueOperations.get("goodsList");
if (!ObjectUtils.isEmpty(goodList)) {return  goodList;
}
// 若 Redis 中没有则从数据库加载
List<GoodsVo> list = goodsService.findGoodVo();
// 设置失效时间 5 min
valueOperations.set("goodsList", list, 5, TimeUnit.MINUTES);return list;

6.2 解决数据库超卖问题以及将下单信息加入缓存

  • 在数据库中将 seckill_order 表中 user_id 字段和 goods_id 字段组成联合唯一索引,解决同一个用户多次秒杀同一件商品。
  • 判定库存为 < 1 时,抛出售空的异常。
  • 将下单信息加入 Redis 缓存,多次购买同一件商品时直接从 Redis 中获取。

7. 加入对象 JSON 缓存,解决数据库超卖问题以及将下单信息加入缓存 Jmeter 压测记录

7.1 获取商品列表测试

设置线程数 5000,每个线程重复请求 10 次。

重复此操作 3 次。

7.1.1 Windows 测试

QPS:3171.3

7.1.2 Linux 测试

QPS:2333.6

7.2 秒杀

设置线程数 1000,每个线程重复请求 10 次。

重复此操作 3 次。

7.2.1 Windows 测试

QPS:1421.1

7.2.2 Linux 测试

QPS:1064.8

7.3 总结


相对于之前没有缓存的情况有明显的提升,并且解决了超卖问题,以及解决了同一个用户只能购买一件同样的商品。

8. 中间件

应用程序(可以是不同编程语言开发)使用某种协议或者说规范与 OS 底层进行通信。这种规范就是中间件,中间件具有跨平台,支持通信,高可用,持久性等特点。

8.1 消息中间件

利用可靠的消息传递机制进行系统和系统直接的通讯,通过提供消息传递和西澳西的排队机制,它可以在分布式系统环境下,扩展进程间的通信。

本质

  • 接收数据
  • 接受请求
  • 存储数据
  • 发送请求等功能的技术服务

同时性能要比普通的服务和技术要高。

组成部分

  1. 消息的协议
  2. 消息的持久化机制
  3. 消息的分发策略
  4. 消息的高可用,高可靠
  5. 消息的容错机制

应用场景

加入有 10W 的并发请求下订单,我们可以在这些订单入库之前把订单请求堆积到消息队列中,让它稳健可靠的入库和执行。

协议

AMQP,高级消息队列协议

8.2 RabbitMQ

RabbitMQ 中消息传递模型的核心思想是:生产者永远不会直接向队列发送任何消息。生产者常常根本不知道消息是否会被传递到任何队列。

生产者只能将消息发送到交换器(exchange)。

8.2.1 交换器(Exchange)

交换器职责:

  • 接收来自生产者的消息
  • 将这些消息推送到队列
  • 交换器必须确切地知道如何处理它接收到的消息
    • 它应该被追加到特定的队列中吗?
    • 它应该被追加到许多队列中吗?
    • 还是应该被丢弃?

以上规则由交换机类型定义,类型有:directtopicheadersfanout

RabbitMQ 默认交换机:

8.2.1.1 Fanout 模式

类似发布订阅,也有点像广播,多个消费者会受到同一个发布者发送的同一个消息。

8.2.1.2 Direct 模式(常用)

有选择地接收信息。

绑定的时候可以采取一个额外的 routingKey 参数。为了避免与 basic_publish 参数相混淆,我们要把它称为绑定键。

Fanout 模式中是将消息传给所有的队列,Direct 模式可以指定不同的路由键的消息发送给指定消费者。

8.2.1.3 Topic 模式(常用)

根据一个模式(主题)接收信息。

可以指定 RoutingKey 包含某个关键词的队列进行发送。带来更多的灵活性。

RoutingKey 中可以有任意多的单词,最多不能超过 255 字节的限制。

  • * 表示匹配一个词
  • # 表示匹配 0 个或者多个词

当一个队列用 # 绑定键绑定时–它将收到所有的消息,不管 RoutingKey 是什么,就好像在 Fanout 交换机中一样。

8.2.1.4 Header 模式

可以指定哪些指定 key 的 value 值进行发送。(用的不多)

9. 接口优化

  1. 系统初始化的时候加载全部商品 id 和库存数量到 Redis,并初始化全部商品库存为空的标记全为 false
  2. 通过 Redis 预减库存,减少数据库的访问,若库存为 0,则将库存为空的标记置为 true
  3. 请求进入队列缓存,异步下单(告诉客户端正在排队中)

10. 使用 Redis 预减库存,使用消息队列通知数据库处理订单,Jmeter 秒杀压测

设置线程数 1000,每个线程重复请求 10 次。

重复此操作 3 次。

10.1 Windows 测试

QPS:2196.5

10.2 Linux 测试

QPS:1784.7

Java 商品抢购系统设计,实现与优化相关推荐

  1. 巨人java生鲜app下载_Java生鲜电商平台-生鲜电商商品中心系统设计与数据库架构(生鲜小程序/APP)...

    Java生鲜电商平台-生鲜电商商品中心系统设计与数据库架构(生鲜小程序/APP) 说明:Java生鲜电商平台-生鲜电商商品中心系统设计与数据库架构(生鲜小程序/APP) 日日鲜-商品中心系统设计 项目 ...

  2. Java商品秒杀抢购模拟双十一基础版

    Java秒杀抢购 需要用到的技术 java多线程 Redis mysql数据库 Quartz定时器 用到的框架: SSM 整体项目结构: 前端页面不用我们写,我这里提源码项目自己下载 链接:https ...

  3. 快递管理系统 java_快递管理基于java物流快递管理系统设计.doc

    快递管理基于java物流快递管理系统设计 快递管理系统 毕业设计论文 题 目 快递管理系统 姓 名 王敏雪 所 属 系 数学与计算机科学 专 业 计算机科学与技术 班 级 07级计科三班 指导教师 张 ...

  4. JAVA点餐系统设计计算机毕业设计Mybatis+系统+数据库+调试部署

    JAVA点餐系统设计计算机毕业设计Mybatis+系统+数据库+调试部署 JAVA点餐系统设计计算机毕业设计Mybatis+系统+数据库+调试部署 本源码技术栈: 项目架构:B/S架构 开发语言:Ja ...

  5. 基于Mysql+JavaSwing的超市商品管理系统设计与实现

    文章来源: 学习通http://www.bdgxy.com/ 目录 1.功能介绍 2.关键代码 2.1 主页功能 2.2 添加商品信息 2.3 数据库设计 商品表 前言: 随着小超市规模的发展不断扩大 ...

  6. HTML+CSS+JavaScript实现的品优购项目源代码,包含首页、登录页面、注册页面、商品秒杀页、商品推文页、商品抢购页等

    本次项目一共实现了7个界面,包括首页.登录页面.注册页面.商品秒杀页.商品推文页.商品抢购页.商品详情页等界面. 完整代码下载地址:HTML+CSS+JavaScript实现的品优购项目源代码 项目展 ...

  7. [附源码]计算机毕业设计JAVA干果在线销售系统设计

    [附源码]计算机毕业设计JAVA干果在线销售系统设计 项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(In ...

  8. java装修设计管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署

    java装修设计管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署 java装修设计管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署 本源码技 ...

  9. 性能调优之Java系统级性能监控及优化

    性能调优之Java系统级性能监控及优化 对于性能调优而言,通常我们需要经过以下三个步骤:1,性能监控:2,性能剖析:3,性能调优 性能调优:通过分析影响Application性能问题根源,进行优化Ap ...

最新文章

  1. mock模拟的数据能增删改查吗_Mock.js模拟数据,脱离后端独立开发,实现增删改查功能...
  2. JavaScript教程——JavaScript 的基本语法(标识符)
  3. mysql rtree_优化体系--MySQL 索引优化(full-text、btree、hash、rtree)
  4. Leecode刷题热题HOT100(14)——最长公共前缀
  5. 客机客座率达到多少,航空公司才能不赔钱
  6. ubuntu 上的python不能解析jpeg,png?
  7. 计算机普通用户禁止修改c盘,win10系统禁止Guest账户访问C盘的设置技巧
  8. Linux下Linpack测试CPU性能的相关参数配置以及执行命令
  9. linux加静态路由命令,LINUX添加静态路由
  10. SSD算法详解 及其 keras实现
  11. 梯度下降算法笔记整理6 - 梯度下降 偏导数及其几何意义
  12. 40G/100G万兆交换机如何选择?
  13. Telegram Android开源源码运行
  14. Messaging——Solace PubSub+
  15. 卡拉兹(Callatz)猜想
  16. android培训总结范文,android开发培训心得总结
  17. 7-5 快乐的尽头 (17 分)
  18. 8个游戏开发工具让你不懂编程也能做游戏
  19. MySQL查询分析器EXPLAIN
  20. Linux ffmpeg 一键安装

热门文章

  1. 0 地道英语口语/发音 | 高效提升攻略
  2. 【软件构造】--Java中的协变与逆变
  3. [内附完整源码和文档] 基于JAVA的合同管理系统
  4. 高性能串口转以太网模块
  5. tcpip协议与服务器的关系,RS232转TCPIP的TCP工作模式选择
  6. 【解决】SX1308无法升压、升压后接上负载电压就被拉低解决办法
  7. Caliburn.Micro开发框架介绍 (Windows phone
  8. 事情处理方法之一:时间错开
  9. 胜为蓝牙适配器驱动_胜为蓝牙适配器驱动
  10. 安卓开发开发规范手册V1.0