欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

【电商秒杀项目】项目总结:Redis实现分布式Session、用户认证、超卖、系统压测、RabbitMQ异步下单、接口限流

时间:2023-04-22

前言:学习自慕课网课程—《Java秒杀系统方案优化 高性能高并发实战》,看完后自己开发了一遍,受益很多,便打算写出来与大家一起分享,本文为自己做完后的项目总结与笔记。

技术栈:Spring Boot、Thymeleaf、MyBatis-Plus、Redis、RabbitMQ

压测工具:JMeter

文章目录

电商秒杀介绍1 用户认证

1.1 用户登录1.2 共享 Session 知识1.3 记录用户凭证 2 功能开发

2.1 商品列表2.2 商品详情2.3 秒杀2.4 订单详情 3 系统压测

3.1 JMeter3.2 自定义变量模拟多用户3.3 正式压测 4 页面优化5 接口优化

5.1 解决超卖5.1 Redis 预减库存减少数据库的访问5.2 内存标记减少 Redis 的访问5.3 RabbitMQ 异步下单 6 安全优化

6.1 秒杀接口地址隐藏6.2 算术验证码6.3 接口限流 电商秒杀介绍

秒杀是在 瞬间击杀 的意思,放在电商中,就是网上竞拍的一种方式;所谓“秒杀”,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。

从技术角度来讲,秒杀主要解决两个问题,一个是并发读,一个是并发写。

并发读的核心优化理念是尽量减少用户到服务端来“读”数据,或者让他们读更少的数据;并发写的处理原则也一样,它要求我们在数据库层面独立出来一个库,做特殊的处理。

另外,电商秒杀系统也要保证高性能、数据一致性、系统高可用性等必要性能,因此实际项目的难度十分之大,会出现许多意料之外的情况,本文学习并总结自慕课网教程,已在前言中写明,下面,就电商秒杀中的几个核心知识点进行项目的练习。

1 用户认证 1.1 用户登录

用户信息加密本项目中采用 MD5 这种加密方式,明文密码需要二次 MD5 加密,当用户注册时,我们在前台校验输入参数成功后,将密码进行 MD5 加密传输到后台,后台经过处理逻辑判断后,如果判定为这些输入参数可以进行注册,那么这时也应该将密码再次加密存入数据库中,只有两次在传输时进行加密,才可以保证用户输入的信息的相对安全的(MD5 存在弱点,也不是绝对不可以破解的),下面为后台的两次加密方法,在本项目中只用 backPassToDBPass 这个方法,前台则也有一个类似的加密方法,用 JS 实现。

登录功能中密码的传输也是一样的。

1.2 共享 Session 知识

分布式环境下 Session 不在服务器集群上互通,有以下几种常见解决方案:

Session复制:优点是无需修改代码,只需要修改 Tomcat 配置,缺点是 Session 同步传输占用内网带宽多台 Tomcat 同步性能指数级下降,Session 占用内存,无法有效水平扩展;

前端存储:优点是不占用服务端内存,缺点是存在安全风险,数据大小受 cookie 限制,占用外网带宽 ;

Session 粘滞:优点是无需修改代码,服务端可以水平扩展,缺点是增加新机器,会重新Hash,导致重新登录,应用重启,需要重新登录;

后端集中存储:优点是安全,容易水平扩展,缺点是增加复杂度,需要修改代码。

将用户登录的信息存入 Redis 中,这样如果是分布式系统就可以通过 Redis 来获取用户信息并认证了,实现了分布式用户认证。

1.3 记录用户凭证

将用户登录的信息存入 Redis 中,这样系统就可以通过 Redis 来获取用户信息并认证了( redis 也可以用集群,用户认证也可以用 JWT token 签名实现用户认证,不用 redis 等等都可以,这个项目只是侧重于秒杀,而这里记录用户凭证信息是为了之后模拟批量用户进行秒杀测试)。

用户初次登录时


由于我们之后每个需要用户认证的方法,都需要 @cookievalue(“userTicket”) String ticket ,因此我们每次都要拿到 ticket ,再去 redis 中判断是否存在用户,等等操作,所以,我们直接将这一步 AOP 化,也就是在直接用 Spring MVC 的参数解析器将其在方法参数中直接完成判断


之后需要用户信息的方法,我们这样写就可以了

2 功能开发 2.1 商品列表

单独创建秒杀表和秒杀订单表

秒杀页面

2.2 商品详情 2.3 秒杀

秒杀活动未开始前,不展示验证码,立即秒杀按钮不可点击

秒杀活动进行中,展示验证码,立即秒杀按钮可点击

秒杀活动结束后,不展示验证码,立即秒杀按钮不可点击

秒杀初步实现思路如下(本文是按照慕课教程循序渐进地编写代码的,因此这里故意不处理并发等问题,让问题暴露出来,方便后续优化的进行),首先一个用户秒杀商品,那么第一件是就是要判断是否还有没有秒杀库存,如果有的话那么也要判断这个用户在本次活动中是否已经秒杀成功过,如果有库存,未重复抢购,那么才可以进行秒杀,而秒杀的业务逻辑是先生成订单,订单为未支付状态,数据库中库存减1,在订单过期前,用户支付成功,那么则生成真正有效订单(生成秒杀订单和统一订单。由于我们要处理高并发问题,所以单独抽出秒杀订单表,但秒杀订单仍属于统一订单表)。

秒杀代码初步实现

生成订单的实现

2.4 订单详情

秒杀成功,数据库的库存也是在我们的预期变化内,这在单机用户下,是没有问题的,多机大量用户时,就是出现超买超卖问题,我们后续进行演示。

3 系统压测 3.1 JMeter

Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具,用于对软件做压力测试。我们接下来用 JMeter 模拟大量用户进行商品的秒杀的场景。

3.2 自定义变量模拟多用户

我们首先在数据库的用户表中插入 5000 个用户,用代码循环实现即可,同时也将两个产品的可秒杀数量都设为10,清空订单表,方便之后的测试。

Redis 中

创建 5000 个用户线程,并携带其 cookie,进行秒杀测试

3.3 正式压测

执行压测,等待测试完成

秒杀商品表出现了超卖现象,库存都成负数了

下图的秒杀订单表就更不用说了,已经五百多条数据了,然而实际我们秒杀库存只有 10 件,因此,秒杀问题如果处理不好,后果是非常严重的,接下来我们展示如何解决超卖问题。

4 页面优化

由于此慕课教程的项目是按单体项目开发的,而现在很多项目已是前后端分离,这里情况不一样了,按这个项目的页面优化来说,那就是将可以静态化的页面静态化,然后直接放入 redis,当然可以 CDN 优化,这里就不展开说了。

5 接口优化 5.1 解决超卖

我们在生成订单的方法中,再加一个库存大于 0 的判断,如果大于 0 ,我们就不应该生成订单(我们之前是在秒杀时判断的库存是否为正数,此处我们再次在生成订单部分再判断),大于 0 的判断,和是否更新这两个操作应属于在一个原子操作内进行。

同时,我们在生成订单时,也将订单信息存入 redis 中,方便之后在秒杀这个大流量的部分判断库存是否大于 0 时,直接在 redis 缓存中获取是否重复抢购,减轻数据库的压力

为了应对重负抢购的情况,我们再在数据库中设置唯一索引,字段为用户 id 与秒杀商品 id 的组合,这样,在最基础的数据层将不会出现重复抢购的记录


再出初始化我们的测试数据,进行压测,发现就不会有超卖问题了

秒杀商品表

秒杀订单表

订单表

5.1 Redis 预减库存减少数据库的访问 5.2 内存标记减少 Redis 的访问 5.3 RabbitMQ 异步下单

秒杀成功后,我们将秒杀信息以消息队列的形式发送给订单生成模块,让其生成订单

发送方

接收方



6 安全优化 6.1 秒杀接口地址隐藏

秒杀开始之前,先去请求接口获取秒杀地址,防止有人预先知道接口后提前准备好不断去请求。

6.2 算术验证码

能起到一定的瞬间压力缓冲作用,因为用户有人算得快,有人算得慢,也能一定程度抵御脚本,不过现在 OCR 技术确实已经很强了。

6.3 接口限流

可以用写一个通用的限流注解,例如同一客户端多长时间范围内,多少次请求就被限流的参数,然后再在拦截器中用反射来获取限流注解,解析其参数,然后去 redis 中判断是否需要限流等等。


Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。