Java高性能高并发秒杀系统设计与优化

本文介绍了一个Java实现的高并发秒杀系统,涵盖了登录实现、分布式Session、秒杀功能、页面优化、接口优化和安全措施。通过使用MD5加密、JSR303参数检验、Redis预减库存、RabbitMQ异步下单和接口限流等技术,提升了系统的性能和安全性。同时,应用了页面缓存、静态化、CDN和Nginx水平扩展以优化用户体验。
摘要由CSDN通过智能技术生成

项目简介

本项目主要是模拟应对大并发场景下,如何完成商品的秒杀,以及针对秒杀场景下为应对大并发所做的优化。

项目地址:https://github.com/noodou/seckill

项目的技术结构如下图所示:

技术结构.png

数据库设计如下图:

这里的数据库设计只是为了模拟秒杀场景,实际的数据库会复杂很多。需要注意的是,因为参与秒杀的只有部分商品,所以单独建立一个miaosha_goods存储于秒杀商品相关的字段。

  • 秒杀用户表:miaosha_user
  • 商品表:goods
  • 参与秒杀的商品表:miaosha_goods
  • 秒杀订单表:miaosha_order
  • 订单表:order_info

数据库设计.png

秒杀场景下主要解决的问题:

  • 分布式会话
  • 用户登录、商品列表、商品详情、订单详情模块
  • 缓存优化
  • 系统压测,测试系统的QPS
  • 信息队列
  • 接口安全

登录实现

登录部分主要有以下几个部分:

  • 明文密码两次MD5处理

  • JSR303参数检验和全局异常处理器

  • 分布式Session

明文密码两次MD5处理

  • 客户端:C_PASS=MD5(明文+固定salt)
  • 服务端:S_PASS=MD5(C_PASS+随机salt)

加密:出于安全考虑

第一次 (在前端加密,客户端):密码加密是(明文密码+固定盐值)生成md5用于传输,目的,由于http是明文传输,当输入密码若直接发送服务端验证,此时被截取将直接获取到明文密码,获取用户信息。

加盐值是为了混淆密码,原则就是明文密码不能在网络上传输。

第二次:在服务端再次加密,当获取到前端发送来的密码后。通过MD5(密码+随机盐值)再次生成密码后存入数据库。

防止数据库被盗的情况下,通过md5反查,查获用户密码。方法是盐值会在用户登陆的时候随机生成,并存在数据库中,这个时候就会获取到。

第二次的目的:
黑客若是同时黑掉数据库,通过解析前端js文件,知道如果md5加密的过程,就知道此时用户的密码。

但是此时我们要是在后端加入随机盐值和传输密码的md5组合,黑客是无法知道通过后端密码加密过程的,从而无法知道密码。

JSR303参数检验和全局异常处理器

JSR303 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解。如@NotNull@Email@Max等。

在这个系统中,我们自定义了一个注解@IsMobile完成手机号码的参数检验,@IsMobile的校验处理器为IsMobileValidator

定义一个全局异常GlobalException和全局异常处理器GlobalExceptionHandler,可以完成系统异常的捕获和异常的统一处理。

分布式Session

在用户登录成功之后,将用户信息存储在redis中,然后生成一个token返回给客户端,这个token为存储在redis中的用户信息的key,这样,当客户端第二次访问服务端时会携带token,首先到redis中获取查询该token对应的用户使用是否存在,这样也就不用每次到数据库中去查询是不是该用户了,从而减轻数据库的访问压力。

秒杀功能的实现

  • 数据库设计

  • 商品列表页

  • 商品详情页

  • 订单详情页

页面优化技术

  • 页面级缓存+URL缓存+对象缓存
  • 页面静态化,前后端分离
  • 静态资源优化
  • CDN优化

页面级缓存+URL缓存+对象缓存

所谓页面缓存,指的是对于服务端的请求,不直接从系统中获取页面资源,而是先从缓存中获取页面资源,如果缓存中不存在页面资源,则系统将渲染页面并存储页面到缓存中,然后将页面返回。

来看商品列表页的请求过程;请求到服务端,服务端查询数据库中的商品列表信息然后存储在Model对象中,Thymeleaf页面获取在Model对象中的商品列表信息然后动态渲染,再返回给客户端。如果每次请求都做这样的工作,势必会对服务器和系统造成一定的压力(系统的压力主要来源于每次Thymeleaf页面获取在Model对象的信息都要渲染一次),所以可以做一个页面级的缓存,减轻数据库和系统的压力。

在本项目中,我们对商品列表页做一个缓存,因为商品列表页的数据相对表话不是太频繁,所以将其缓存在redis中,这样不用每次都查询数据库中的商品信息,然后再使用Thymeleaf渲染返回,而是直接从redis中返回。另外,由于商品列表页请求返回的是html,所以这里使用ThymeleafViewResolver手动渲染页面,这样就可以将页面直接通过系统返回给客户端。(详细过程在edu.uestc.controller.GoodsListController#toList中)。

而所谓URL缓存,实际上和页面缓存是一样的,在本项目中,我们对商品详情页做了缓存,商品详情页的请求需要goodsId,也就是说,对每一个goodsId都做了一个缓存,其他的和商品列表页的缓存思路是一致的,只不过商品取详情页是需要动态的根据goodsId来取。

通过上面的缓存差异可知,URL缓存和页面缓存的不同之处在于,URL缓存需要根据URL中的参数动态地取缓存,而页面缓存则不需要。

一般来讲,URL缓存和页面缓存的缓存时间都比较短。在本项目中,我们设置商品详情页</

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值