RecYan / SecKillPlus

:rocket::rocket::rocket:SecKill改进版

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

系统介绍

本系统是使用SpringBoot开发的高并发限时秒杀系统,实现基本的登录、查看商品列表、秒杀、下单等功能。并且针对高并发情况作了页面级、服务级和安全级的优化,提升系统的并发度。

秒杀优化方向

  1. 将请求尽量拦截在系统上游

  2. 充分利用Redis缓存

技术点

1. 两次MD5加密

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

好处:

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

2. 分布式session

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

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

3. JSR303自定义参数验证

使用JSR303和自定义校验器[@Constraint用validatedBy自定义校验规则],实现对用户手机、密码的验证,使得验证逻辑从业务代码中脱离出来。

4. 全局异常统一处理

使用@ControllerAdvice对Controller层进行“切面”环绕,使用@ExceptionHandler进行具体的业务的织入来,实现自定义异常拦截器,拦截所有异常,并对各种异常进行相应的处理,有利于对异常进行统一维护管理。

5. 页面缓存 + 对象缓存

  1. 页面缓存:通过ThymeleafViewResolver手动渲染详情页面并缓存到redis,减少服务端请求连接数。
  2. 对象缓存:包括对用户信息、商品信息、订单信息和token等数据进行缓存,利用缓存来减少对数据库的访问,加快查询速度。

6. 页面静态化

对商品详情页和订单详情页进行静态化处理,并写入redis缓存,动态数据通过Ajax请求从服务端获取,实现前后端分离,打开速度有明显提高

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

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

实现:

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

8. 解决超卖问题

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

9. 秒杀地址隐藏+数学公式验证码防刷

描述:秒杀开始时,输入正确的验证码后,用户点击秒杀按钮,系统会请求生成秒杀地址的接口,生成用户商品认证信息[MD5(salt+userId+goodsId)],接口判断认证后才会返回真正的秒杀地址,[userId+goodsId]保证了每个用户的秒杀地址是唯一的。

优点:

  1. 秒杀地址隐藏防止恶意的机器人和爬虫刷单
  2. 图形验证码分散用户的请求

实现:

秒杀地址隐藏

  1. 点击秒杀按钮,服务端接口收到传参[goodsId, userId]同时使用MD5加salt对其进行加密,生成一个随机数[用户商品验证信息],写入redis缓存,并返回给前端

  2. 前端使用该随机数拼上真正的秒杀url,去请求秒杀接口

  3. 后端接口收到请求,先从缓存中取出随机数缓存,验证正确后,才能进入秒杀逻辑,否则返回失败信息

图形验证码

  1. 前端通过把商品id作为参数调用服务端创建验证码接口

  2. 服务端根据前端传过来的商品id和用户id生成验证码,并将商品id+用户id作为key,生成的验证码作为value存入redis,同时将生成的验证码输入图片写入imageIO让前端展示

  3. 将用户输入的验证码与根据商品id+用户id从redis查询到的验证码对比,相同就返回验证成功,进入秒杀;不同或从redis查询的验证码为空都返回验证失败,刷新验证码重试

10. 自定义注解拦截器实现接口限流

自定义类实现HandlerInterceptor接口在preHandler方法中实现限制规则,限制用户在规定的时间内最多点击maxCount次秒杀按钮,使用限流来进行限制访问量,当达到限流阀值maxCount时,后续请求会被降级;降级后的处理方案可以是:返回排队页面(高峰期访问太频繁,等一会重试)。

About

:rocket::rocket::rocket:SecKill改进版


Languages

Language:Java 72.5%Language:HTML 23.5%Language:TSQL 2.9%Language:JavaScript 1.1%