基于Spring Boot + Redis + RabbitMQ的高性能本地生活服务平台,类似大众点评的功能实现
黑马点评是一个完整的本地生活服务平台,提供商铺查询、优惠券秒杀、用户社交、博客分享等功能。项目重点展示了Redis缓存、RabbitMQ消息队列、分布式锁等技术在实际业务场景中的应用,并针对高并发场景进行了深度优化。
- ✅ 用户体验大幅提升:
- 新增专门的头像上传接口,支持文件选择和自动更新
- 修复用户信息更新后前端不显示的问题
- 完善Redis缓存同步机制,确保数据一致性
- ✅ 登录系统优化:
- 修复登录时的NullPointerException问题
- 修复登出时Redis Key错误删除的问题
- 完善Token管理和会话处理
- ✅ 消息队列升级: 从Redis Stream迁移到RabbitMQ,提升消息处理的可靠性和监控能力
- ✅ 错误处理优化: 完善了Lua脚本的nil值检查和RabbitMQ的通道错误处理
- ✅ 数据管理增强: 新增秒杀数据初始化和清理功能
- ✅ 监控日志完善: 添加详细的调试日志,便于问题排查
| 技术 | 版本 | 说明 |
|---|---|---|
| 后端框架 | ||
| Spring Boot | 2.7.4 | 主框架 |
| MyBatis Plus | 3.5.2 | ORM框架 |
| Spring AMQP | 2.7.4 | RabbitMQ集成 |
| 数据存储 | ||
| MySQL | 8.0+ | 关系型数据库 |
| Redis | 6.0+ | 缓存数据库 |
| 消息队列 | ||
| RabbitMQ | 3.8+ | 消息队列中间件 |
| 工具库 | ||
| Hutool | 5.8.8 | Java工具库 |
| Redisson | 3.17.7 | Redis分布式锁 |
| Lombok | 1.18.30 | 代码简化 |
- 手机验证码登录: 基于Redis的验证码存储和校验,支持Token自动续期
- 用户信息管理:
- 用户资料编辑(昵称、个人介绍、性别、城市、生日)
- 头像上传: 专门的头像上传接口,自动更新数据库和Redis缓存
- 实时同步: 信息更新后前端立即显示,数据库与缓存保持一致
- 会话管理: 完善的登录/登出机制,正确的Redis Key管理
- 签到系统: 基于Redis BitMap的连续签到统计
- 商铺信息查询: 支持分类、地理位置查询
- 缓存策略:
- 缓存穿透解决方案(空值缓存)
- 缓存雪崩解决方案(TTL随机化)
- 缓存击穿解决方案(互斥锁、逻辑过期)
- 秒杀功能: 高并发秒杀优惠券,支持库存预扣减
- 分布式锁: 基于Redis + Redisson的分布式锁防止超卖
- 异步处理: Lua脚本 + RabbitMQ消息队列优化性能
- 一人一单: 防止用户重复下单,支持幂等性处理
- 消息可靠性: RabbitMQ死信队列 + 重试机制保证消息不丢失
- 数据管理: 自动初始化秒杀库存,清理过期数据
- 博客发布: 用户可发布图文博客
- 点赞功能: 基于Redis ZSet的点赞排行榜
- 关注系统: 用户关注/取关,共同关注查询
- Feed流: 基于推模式的关注用户博客推送
- 附近商铺: 基于Redis GEO的地理位置查询
- 距离计算: 自动计算用户与商铺的距离
hmdp/
├── src/main/java/com/hmdp/
│ ├── HmDianPingApplication.java # 启动类
│ ├── config/ # 配置类
│ │ ├── MvcConfig.java # MVC配置
│ │ ├── MybatisConfig.java # MyBatis配置
│ │ ├── RedissonConfig.java # Redisson配置
│ │ ├── RabbitMQConfig.java # RabbitMQ配置
│ │ ├── UploadConfig.java # 文件上传配置
│ │ └── WebExceptionAdvice.java # 全局异常处理
│ ├── controller/ # 控制器层
│ │ ├── BlogController.java # 博客控制器
│ │ ├── FollowController.java # 关注控制器
│ │ ├── ShopController.java # 商铺控制器
│ │ ├── UserController.java # 用户控制器
│ │ ├── VoucherController.java # 优惠券控制器
│ │ └── VoucherOrderController.java # 订单控制器
│ ├── dto/ # 数据传输对象
│ │ ├── LoginFormDTO.java # 登录表单
│ │ ├── Result.java # 统一返回结果
│ │ ├── ScrollResult.java # 滚动分页结果
│ │ └── UserDTO.java # 用户信息
│ ├── entity/ # 实体类
│ │ ├── Blog.java # 博客实体
│ │ ├── Follow.java # 关注关系
│ │ ├── Shop.java # 商铺实体
│ │ ├── User.java # 用户实体
│ │ ├── Voucher.java # 优惠券实体
│ │ └── VoucherOrder.java # 订单实体
│ ├── interceptor/ # 拦截器
│ │ ├── LoginInterceptor.java # 登录拦截器
│ │ └── RefreshTokenInterceptor.java # Token刷新拦截器
│ ├── mapper/ # 数据访问层
│ │ ├── BlogMapper.java # 博客Mapper
│ │ ├── ShopMapper.java # 商铺Mapper
│ │ ├── UserMapper.java # 用户Mapper
│ │ └── VoucherOrderMapper.java # 订单Mapper
│ ├── listener/ # 消息监听器
│ │ └── SeckillOrderListener.java # 秒杀订单消息监听器
│ ├── service/ # 服务接口层
│ │ ├── IBlogService.java # 博客服务接口
│ │ ├── IShopService.java # 商铺服务接口
│ │ ├── IUserService.java # 用户服务接口
│ │ ├── IVoucherOrderService.java # 订单服务接口
│ │ ├── MessageProducerService.java # 消息生产者服务接口
│ │ └── SeckillDataInitService.java # 秒杀数据初始化服务接口
│ ├── service/impl/ # 服务实现层
│ │ ├── BlogServiceImpl.java # 博客服务实现
│ │ ├── ShopServiceImpl.java # 商铺服务实现
│ │ ├── UserServiceImpl.java # 用户服务实现
│ │ ├── VoucherOrderServiceImpl.java # 订单服务实现
│ │ ├── MessageProducerServiceImpl.java # 消息生产者服务实现
│ │ └── SeckillDataInitServiceImpl.java # 秒杀数据初始化服务实现
│ └── utils/ # 工具类
│ ├── CacheClient.java # 缓存工具类
│ ├── ILock.java # 分布式锁接口
│ ├── SimpleRedisLock.java # Redis分布式锁实现
│ ├── RedisIdWorker.java # Redis ID生成器
│ ├── RedisConstants.java # Redis常量
│ └── UserHolder.java # 用户上下文
├── src/main/resources/
│ ├── application.yaml # 应用配置
│ ├── mapper/ # MyBatis映射文件
│ ├── seckill.lua # 秒杀Lua脚本(已优化nil值检查)
│ └── unlock.lua # 解锁Lua脚本
├── hmdp.sql # 数据库脚本
├── rabbitmq-setup.md # RabbitMQ安装指南
└── pom.xml # Maven配置
- JDK 11+
- Maven 3.6+
- MySQL 8.0+
- Redis 6.0+
- RabbitMQ 3.8+ (推荐使用Docker)
- 克隆项目
git clone <repository-url>
cd hmdp- 数据库初始化
# 创建数据库
mysql -u root -p
CREATE DATABASE hmdp;
# 导入数据
mysql -u root -p hmdp < hmdp.sql- 配置文件
修改
src/main/resources/application.yaml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/hmdp
username: your_username
password: your_password
redis:
host: localhost
port: 6379
password: your_redis_password # 如果有密码
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest- 启动Redis
redis-server- 启动RabbitMQ
# 使用Docker(推荐)
docker run -d --name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=guest \
rabbitmq:3-management
# 或者直接启动(如果已安装)
rabbitmq-server- 初始化秒杀数据
# 启动应用后,初始化秒杀券库存
curl -X POST http://localhost:8081/voucher-order/init/all- 运行项目
mvn spring-boot:run- 访问应用
- 后端API: http://localhost:8081
- RabbitMQ管理界面: http://localhost:15672 (guest/guest)
- 接口文档: 查看各Controller类的接口定义
| 功能 | Redis数据结构 | 应用场景 |
|---|---|---|
| 用户登录 | Hash | 存储验证码和用户Token,支持用户信息缓存 |
| 用户信息缓存 | Hash | 缓存用户基本信息,支持实时更新同步 |
| 商铺缓存 | String | 缓存商铺信息,解决缓存穿透/击穿/雪崩 |
| 点赞排行 | ZSet | 博客点赞用户排序 |
| 关注列表 | Set | 存储用户关注关系,计算共同关注 |
| 签到统计 | BitMap | 用户连续签到天数统计 |
| 附近商铺 | GEO | 地理位置查询 |
| 秒杀库存 | String + Lua | 原子性库存扣减(已优化nil值检查) |
| 分布式锁 | String + Lua | 防止并发问题 |
| Feed流 | ZSet | 关注用户博客时间线 |
| 消息去重 | String | 防止RabbitMQ消息重复处理 |
| 组件 | 说明 | 特性 |
|---|---|---|
| 秒杀订单队列 | seckill.order.queue |
处理秒杀订单异步创建 |
| 死信队列 | seckill.order.dlx.queue |
处理失败消息,支持人工介入 |
| 消息重试 | 自动重试机制 | 最多重试3次,失败后进入死信队列 |
| 消息确认 | 手动ACK | 确保消息处理成功后才确认 |
| 幂等处理 | Redis去重 | 防止消息重复处理 |
- 基于Redis的SET NX EX命令
- Lua脚本保证原子性
- 自动续期机制(Redisson)
- 防止锁误删除
- 缓存穿透: 空值缓存 + 布隆过滤器
- 缓存击穿: 互斥锁 + 逻辑过期
- 缓存雪崩: TTL随机化 + 多级缓存
用户请求 → Lua脚本(库存检查) → RabbitMQ队列 → 异步处理 → 数据库
↓ ↓ ↓ ↓
参数校验 原子性操作 消息持久化 订单入库
↓ ↓ ↓ ↓
返回结果 防止超卖 重试机制 完成订单
秒杀流程优化:
- 预检查: Lua脚本原子性检查库存和用户资格
- 异步处理: RabbitMQ解耦,提升响应速度
- 可靠性保证: 消息持久化 + 死信队列 + 重试机制
- 幂等性: Redis去重 + 分布式锁防止重复处理
| 接口 | 方法 | 说明 |
|---|---|---|
/user/code |
POST | 发送手机验证码 |
/user/login |
POST | 手机验证码登录 |
/user/logout |
POST | 用户登出 |
/user/me |
GET | 获取当前用户信息 |
/user/update |
PUT | 更新用户基本信息(昵称等) |
/user/info/update |
PUT | 更新用户详细信息(城市、介绍等) |
/user/avatar/upload |
POST | 头像上传(文件上传+数据库更新) |
/user/info/{id} |
GET | 获取用户详细信息 |
/user/sign |
POST | 用户签到 |
/user/sign/count |
GET | 获取签到统计 |
| 接口 | 方法 | 说明 |
|---|---|---|
/voucher-order/seckill/{id} |
POST | 秒杀优惠券 |
/voucher-order/init/{voucherId} |
POST | 初始化单个秒杀券库存 |
/voucher-order/init/all |
POST | 批量初始化所有秒杀券库存 |
/voucher-order/clean/expired |
POST | 清理过期秒杀数据 |
| 接口 | 方法 | 说明 |
|---|---|---|
/shop/debug/types |
GET | 查看商铺类型分布 |
/shop/debug/geo/{typeId} |
GET | 检查Redis GEO数据 |
/shop/init/geo |
POST | 初始化商铺地理位置数据 |
/upload/debug/config |
GET | 检查文件上传配置 |
本项目来自黑马程序员Redis教程,并进行了深度优化:
- 视频教程: Redis入门到实战
- 实战篇从P24开始
- master: 完整版代码,包含RabbitMQ优化和错误修复
- init: 初始化代码,适合跟着教程学习开发
- QPS优化: 通过Redis预检查 + 异步处理,支持万级QPS
- 超卖防护: Lua脚本原子性操作 + 分布式锁双重保障
- 用户体验: 快速响应,异步处理订单创建
- 消息持久化: RabbitMQ确保消息不丢失
- 重试机制: 自动重试3次,失败进入死信队列
- 监控告警: 通过管理界面实时监控队列状态
- 多级缓存: 本地缓存 + Redis缓存
- 缓存一致性: 更新策略 + 过期时间控制
- 性能监控: 详细的缓存命中率统计
- 头像上传: 一键上传,自动更新数据库和缓存,前端实时显示
- 信息同步: 用户信息更新后立即同步到Redis缓存和前端显示
- 会话管理: 完善的登录状态管理,Token自动续期
- 错误处理: 友好的错误提示和异常处理机制
- 错误处理: 完善的异常处理和降级策略
- 日志监控: 详细的业务日志和性能指标
- 数据管理: 自动化的数据初始化和清理
- 缓存一致性: 数据库更新与Redis缓存实时同步
| 指标 | 数值 | 说明 |
|---|---|---|
| 秒杀QPS | 10,000+ | Redis + RabbitMQ异步处理 |
| 响应时间 | <100ms | Lua脚本快速预检查 |
| 消息处理 | 99.9%+ | RabbitMQ可靠性保证 |
| 缓存命中率 | 95%+ | 多级缓存策略 |
如有问题,请参考原教程或提交Issue。
感谢黑马程序员提供的优秀教程,本项目在原有基础上进行了深度优化和扩展。
克隆完整项目
git clone https://github.com/sgahch/hmdp.git
切换分支
git checkout init
错误: Connection refused 或 AMQP connection error
解决方案:
# 检查RabbitMQ是否启动
docker ps | grep rabbitmq
# 如果未启动,执行启动命令
docker run -d --name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:3-management错误码: 返回错误码3
解决方案:
# 初始化秒杀券库存
curl -X POST http://localhost:8081/voucher-order/init/all
# 或初始化单个秒杀券
curl -X POST http://localhost:8081/voucher-order/init/{voucherId}错误: attempt to compare nil with number
解决方案: 已在最新版本中修复,确保使用最新的seckill.lua脚本
错误: 上传目录不存在
解决方案: 检查nginx目录配置,确保路径正确
# 检查上传配置
curl http://localhost:8081/upload/debug/config解决方案: 初始化商铺地理位置数据
curl -X POST http://localhost:8081/shop/init/geo错误: 更新昵称或头像后前端不显示最新信息
解决方案:
- 检查Redis缓存是否正常工作
- 确保使用最新版本的代码(已修复缓存同步问题)
- 清除浏览器缓存并重新登录
错误: "头像上传失败"或文件上传错误
解决方案:
# 检查上传目录权限
ls -la nginx-1.18.0/html/hmdp/imgs/
# 确保目录存在且可写
mkdir -p nginx-1.18.0/html/hmdp/imgs/
chmod 755 nginx-1.18.0/html/hmdp/imgs/错误: 登录过程中空指针异常
解决方案: 已在最新版本中修复,确保UserDTO中的字段正确处理null值