引言
在当前互联网应用程序的开发进程中,高并发场景下的系统性能优化始终是开发者要应对的重要课题。特别是在像电商、零售这类涉及大量商品库存管理的场景中,怎样高效处理用户的购买请求、确保库存数据的一致性以及系统的响应速度,成了系统设计的关键所在。此项目基于Spring Boot框架,结合Redis缓存技术,构建了一个图书购买与库存管理系统,着重解决高并发环境下库存扣减、购买操作等核心业务场景。
Redis作为高性能的内存数据库,具备读写速度快、支持丰富数据结构、支持事务和持久化等特性,特别适合用于缓存热点数据、处理高并发的库存扣减等场景。本系统把图书库存数据存储在Redis中,借助其高速读写能力来处理用户的购买和取消购买操作,同时通过数据库持久化存储用户的购买记录,实现了缓存与数据库的双重数据保障。
本文将会详细介绍该系统的设计与实现,涵盖前后端交互接口、整体代码逻辑、Redis配置、后端各层代码讲解以及前端页面实现,最后对整个系统进行总结与优化思考。
1. 前后端交互接口
1.1 接口设计概述
该系统达成了图书购置、批量购置、取消购置、批量取消购置以及分页查询等关键功能,前后端通过RESTful API开展交互。接口设计遵循简洁、清晰的原则,以保证前端页面能够高效地与后端服务进行数据交换。
1.2 核心接口详情
1.2.1 购买图书接口
- 接口URL :
/specialNormal/buy
- 请求方法 :POST
- 请求参数 :
bookId
:整数类型,要购买的图书ID
- 请求头 :需包含用户会话信息(通过HttpSession获取用户信息)
- 响应结果 :
- 成功:
{"status":"SUCCESS"}
- 失败:
{"status":"FAIL"}
- 成功:
- 接口功能 :处理单个图书的购买请求,包含库存检查、库存扣减、更新用户购买记录等逻辑
1.2.2 批量购买图书接口
- 接口URL :
/specialNormal/batchPurchaseSpecialBook
- 请求方法 :POST
- 请求参数 :
idList
:整数列表,要购买的图书ID列表
- 请求头 :需包含用户会话信息
- 响应结果 :
- 成功:
{"status":"SUCCESS"}
- 失败:
{"status":"FAIL"}
- 成功:
- 接口功能 :处理多个图书的批量购买请求,通过循环调用单个购买接口来实现
1.2.3 取消购买图书接口
- 接口URL :
/specialNormal/noBuy
- 请求方法 :POST
- 请求参数 :
bookId
:整数类型,要取消购买的图书ID
- 请求头 :需包含用户会话信息
- 响应结果 :
- 成功:
{"status":"SUCCESS"}
- 失败:
{"status":"FAIL"}
- 成功:
- 接口功能 :处理单个图书的取消购买请求,包含恢复库存、更新用户购买记录等逻辑
1.2.4 批量取消购买图书接口
- 接口URL :
/specialNormal/batchNoBuyBook
- 请求方法 :POST
- 请求参数 :
idList
:整数列表,要取消购买的图书ID列表
- 请求头 :需包含用户会话信息
- 响应结果 :
- 成功:
{"status":"SUCCESS"}
- 失败:
{"status":"FAIL"}
- 成功:
- 接口功能 :处理多个图书的批量取消购买请求,通过循环调用单个取消购买接口来实现
1.2.5 分页查询特价购物车接口
- 接口URL :
/specialNormal/getSpecialBookListOfNormalByPage
- 请求方法 :GET
- 请求参数 :
currentPage
:整数类型,当前页码pageSize
:整数类型,每页显示数量
- 请求头 :需包含用户会话信息
- 响应结果 :
- 成功:
{"status":"SUCCESS","data":{"total":总记录数,"bookInfoList":图书列表,"pageRequest":分页参数}}
- 失败:
{"status":"FAIL","errorMessage":"错误信息"}
- 成功:
- 接口功能 :依据分页参数查询用户特价购物车中的图书列表,支持分页展示
1.3 接口设计特点
- 无状态设计 :接口设计遵循RESTful原则,尽量保持无状态,通过会话(HttpSession)获取用户信息
- 参数校验 :每个接口都进行了基本的参数校验,以确保输入数据的合法性
- 统一响应格式 :使用统一的Result类作为响应结构,包含状态码、错误信息和数据内容
- 批量操作支持 :提供批量购买和批量取消购买接口,提升用户操作效率
- 分页查询 :实现分页查询接口,优化大数据量下的前端展示性能
这些接口设计不仅满足了基本的业务需求,还考虑了系统的可扩展性和性能优化,为后续功能迭代奠定了良好的基础。
2. 整体的代码逻辑
2.1 系统初始化流程
系统启动之时,会自动启动数据初始化流程,将数据库中的图书数据同步到Redis缓存中,具体流程如下:
- InitializingBean触发 :
DataInitService
实现了InitializingBean
接口,在容器初始化完成后,afterPropertiesSet()
方法会被自动调用 - 数据同步服务调用 :在
afterPropertiesSet()
方法中,调用DataSyncService
的syncMysqlToRedis()
方法 - 数据库查询 :
DataSyncService
通过RedisMapper
从数据库查询图书信息 - Redis批量存储 :将查询到的图书信息批量存入Redis,使用
bookInfoId:{id}
的格式作为Key,Value使用JSON序列化的图书对象
2.2 购买操作整体逻辑
当用户执行购买操作时,系统会按照以下流程进行处理:
- 参数校验 :Controller层对输入的图书ID和用户会话进行校验
- Redis库存检查 :Service层从Redis中查询对应图书的库存信息
- 库存扣减 :若库存充足,减少Redis中的库存数量
- 用户购买记录处理 :检查用户是否已购买过该图书
- 首次购买:向用户特价图书表插入记录
- 重复购买:更新用户特价图书表中的购买数量
- 返回结果 :根据操作结果返回成功或失败响应
2.3 取消购买操作整体逻辑
当用户执行取消购买操作时,系统会按照以下流程进行处理:
- 参数校验 :Controller层对输入的图书ID和用户会话进行校验
- 用户购买记录查询 :Service层查询用户特价图书表中该图书的购买记录
- 购买数量更新 :若存在购买记录且数量大于0,减少用户特价图书表中的购买数量
- Redis库存恢复 :增加Redis中对应图书的库存数量
- 返回结果 :根据操作结果返回成功或失败响应
2.4 批量操作逻辑
批量购买和批量取消购买操作的整体逻辑类似,主要区别在于:
- 循环处理 :批量操作会遍历ID列表,逐个调用单个操作接口
- 异常处理 :批量操作中若某个单操作失败,会记录错误但继续处理后续操作
- 结果汇总 :最终返回整体操作结果,成功或失败
2.5 分页查询逻辑
分页查询用户特价购物车的流程如下:
- 参数校验 :校验分页参数的合法性(当前页码、每页数量)
- 数据库查询 :根据分页参数查询用户特价图书表中的记录
- 数据处理 :将查询结果中的状态码转换为可读的状态名称(通过枚举类)
- 分页结果封装 :将查询结果和分页信息封装为
PageResult
对象 - 返回结果 :将分页结果返回给前端页面展示
2.6 数据一致性保证
本系统在设计时考虑了数据一致性问题,主要通过以下方式保证:
- 缓存与数据库双重存储 :图书库存数据存储在Redis中,用户购买记录存储在数据库中
- 操作原子性 :购买和取消购买操作中,Redis操作和数据库操作保持一致
- 异常处理 :在操作失败时,通过异常捕获和处理确保不会出现数据不一致的情况
- 初始化同步 :系统启动时自动将数据库数据同步到Redis,保证初始数据一致
通过以上逻辑设计,系统实现了高效的图书购买与库存管理功能,同时保证了数据的一致性和系统的稳定性。
3. Redis配置
3.1 Redis配置类设计
在Spring Boot项目中,我们通过RedisConfig
类来配置Redis相关参数,该类使用@Configuration
注解标识为配置类,并通过@Bean
注解注册RedisTemplate
bean。以下是完整的配置代码:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建redisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 创建json序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置Key的序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//设置value的序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
3.2 序列化方式选择
在Redis配置中,序列化方式的选取至关重要,它直接影响数据在Redis中的存储格式和读取效率。本系统采用了以下序列化策略:
- Key的序列化 :使用
RedisSerializer.string()
对Key进行序列化,将Key转换为字符串格式,保证Key的可读性和唯一性 - Value的序列化 :使用
GenericJackson2JsonRedisSerializer
对Value进行序列化,将Java对象转换为JSON格式存储在Redis中- 优点:JSON格式具有良好的跨平台性和可读性,便于调试和问题排查
- 缺点:相比二进制序列化,JSON序列化会增加一定的存储空间,但在可读性和兼容性上更有优势
3.3 RedisTemplate配置解析
RedisTemplate
是Spring Data Redis提供的核心操作类,通过配置RedisTemplate
可以自定义Redis的连接方式、序列化方式等。以下是配置中的关键步骤:
- 创建RedisTemplate实例 :通过构造函数创建
RedisTemplate<String, Object>
实例 - 设置连接工厂 :通过
setConnectionFactory()
方法注入RedisConnectionFactory
,该工厂负责创建Redis连接 - 配置序列化器 :
- 设置Key和HashKey的序列化器为字符串序列化器
- 设置Value和HashValue的序列化器为JSON序列化器
- 返回配置好的RedisTemplate :将配置完成的
RedisTemplate
作为bean注册到Spring容器中
3.4 Redis配置的最佳实践
本配置遵循了以下Redis使用的最佳实践:
- 分开配置Key和Value的序列化 :Key使用字符串序列化保证可读性,Value使用JSON序列化保证对象完整性
- 使用连接工厂 :通过连接工厂管理Redis连接,提高连接的复用性和管理效率
- 模板化操作 :使用
RedisTemplate
封装Redis操作,避免直接操作Redis连接,提高代码的可维护性 - 通用Object类型 :将Value的类型设为Object,提高模板的通用性,支持存储不同类型的对象
3.5 Redis配置的扩展点
若需要对Redis配置进行扩展,可以考虑以下方向:
- 连接池配置 :在
RedisConnectionFactory
中可以配置连接池的大小、超时时间等参数 - Redis集群配置 :若需要支持Redis集群,可以配置集群节点信息
- 序列化方式优化 :若对性能要求极高,可以考虑使用二进制序列化(如JdkSerializationRedisSerializer)
- 缓存过期策略 :可以在存储数据时设置过期时间,避免无效数据长期占用内存
通过合理的Redis配置,本系统实现了高效的Redis操作,为高并发的库存管理和购买操作提供了性能保障。
4. 后端代码讲解
4.1 Controller层
Controller层作为前端请求的切入点,负责接收请求、参数校验、调用Service层处理业务逻辑,并将结果返回给前端。本系统的Controller层由SpecialDealNormalController
类实现,主要包含以下几个核心方法:
4.1.1 购买图书方法
@RequestMapping("/buy")
public Result buy(Integer bookId, HttpSession session) {
Result result = new Result();
if (bookId == null) {
result.setStatus(ResultStatus.FAIL);
return result;
}
// 从session中提取用户
UserInfo userInfo = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);
if (userInfo == null) {
log.info("提取session中的userInfo失败");
result.setStatus(ResultStatus.FAIL);
return result;
}
boolean b = specialDealNormalService.buy(bookId, userInfo.getId());
if (b == true) {
result.setStatus(ResultStatus.SUCCESS);
return result;
}
result.setStatus(ResultStatus.FAIL);
return result;
}
该方法的处理流程:
- 校验图书ID是否为空
- 从Session中获取用户信息,校验用户是否登录
- 调用Service层的buy方法处理购买逻辑
- 根据Service层的返回结果设置响应状态
4.1.2 批量购买图书方法
@RequestMapping("/batchPurchaseSpecialBook")
public Result batchPurchaseSpecialBook(@RequestParam("idList") List<Integer> idList, HttpSession session) {
log.info("----------------------batchPurchaseSpecialBook");
Result result = new Result();
if (idList == null || idList.size() == 0) {
result.setStatus(ResultStatus.FAIL);
return result;
}
try {
for (Integer id : idList) {
buy(id, session);
}
} catch (Exception e) {
log.info("批量购买错误:" + e.getMessage());
result.setStatus(ResultStatus.FAIL);
return result;
}
result.setStatus(ResultStatus.SUCCESS);
return result;
}
批量购买方法的特点:
- 校验ID列表是否为空或为空列表
- 遍历ID列表,逐个调用单个购买方法
- 使用try-catch捕获可能的异常,保证部分失败不影响整体流程
- 最终返回批量操作的整体结果
4.1.3 取消购买图书方法
```java
@RequestMapping("/noBuy")
public
文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/12746.html