秒杀系统详细教程
1. 项目概述
下载代码 以网上开源仓库seckill_parent为例将项目下载到IDEA里面
1.1 项目简介
这是一个基于Spring Cloud微服务架构的秒杀系统,采用分布式设计,能够应对高并发场景下的商品抢购需求。
1.2 业务特点
- 瞬时并发量大:大量用户在同一时间抢购,网站流量瞬间激增
- 库存少:低价限量商品,访问数量远大于库存数量
- 业务流程简单:流程短,立即购买、下订单、减库存
- 前期预热:未开启活动时显示倒计时,只能访问不能下单
1.3 技术栈
| 技术 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8 | Java运行环境 |
| Maven | 3.5.2 | 项目构建工具 |
| MySQL | 5.7 | 数据库 |
| Spring Boot | 2.0.4.RELEASE | 应用框架 |
| Spring Cloud | Finchley.M9 | 微服务框架 |
| Redis | 3.2 | 缓存数据库 |
| RabbitMQ | 3.7.14 | 消息队列 |
2. 系统架构设计
2.1 整体架构
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端页面 │ │ 网关服务 │ │ 注册中心 │
│ (Client) │◄──►│ (Zuul) │◄──►│ (Eureka) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ 商品服务 │ │ 订单服务 │ │ 库存服务 │
│ (Stock) │ │ (Order) │ │ (Storage) │
└──────────────┘ └───────────┘ └─────────────┘
│ │ │
┌───────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ 用户服务 │ │ 时间服务 │ │ 消息队列 │
│ (User) │ │ (Time) │ │ (RabbitMQ) │
└──────────────┘ └───────────┘ └─────────────┘
2.2 微服务模块说明
| 服务名称 | 端口 | 功能描述 |
|---|---|---|
| seckill_server | 9000 | Eureka注册中心 |
| seckill_zuul | 8080 | 网关服务,统一入口 |
| seckill_client | 3000 | 前端页面服务 |
| seckill_stock | 7000 | 商品服务,管理商品和秒杀政策 |
| seckill_order | 4000 | 订单服务,处理订单创建和支付 |
| seckill_storage | 6001 | 库存服务,管理库存扣减 |
| seckill_user | 5000 | 用户服务,用户管理 |
| seckill_time | 8000 | 时间服务,提供统一时间 |
3. 核心功能实现
3.1 秒杀政策管理
3.1.1 数据库设计
-- 秒杀政策表
CREATE TABLE `tb_limit_policy` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`sku_id` bigint(20) NOT NULL COMMENT 'skuid',
`quanty` bigint(20) DEFAULT NULL COMMENT '数量',
`price` bigint(20) DEFAULT NULL COMMENT '秒杀价格',
`begin_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间',
`end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '结束时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.1.2 政策创建流程
@Transactional
public Map<String,Object> insertLimitPolicy(LimitPolicy limitPolicy){
// 1. 参数验证
if(limitPolicy == null){
return errorResult("数据传入错误");
}
// 2. 写入数据库
int flag = skuDao.insertLimitPolicy(limitPolicy);
if(flag != 1){
return errorResult("数据执行失败");
}
// 3. 计算有效期并写入Redis
long diff = (end_time.getTime() - now_time.getTime())/1000;
redisTemplate.opsForValue().set("LIMIT_POLICY_"+sku_id, limitPolicy, diff, TimeUnit.SECONDS);
return successResult();
}
3.2 秒杀订单创建
3.2.1 核心逻辑
public Map<String, Object> createOrder(String sku_id, BigInteger user_id) {
// 1. 参数验证
if (sku_id == null || sku_id.equals("")){
return errorResult("前端又传错了!");
}
// 2. 从Redis获取秒杀政策
LimitPolicy limitPolicy = (LimitPolicy) redisTemplate.opsForValue().get("LIMIT_POLICY_"+sku_id);
if(StringUtils.isEmpty(limitPolicy)){
return errorResult("活动已经过期!");
}
// 3. 时间验证
String now = restTemplate.getForObject("http://localhost:8000/getTime", String.class);
Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime() <= now_time.getTime() && now_time.getTime() <= end_time.getTime()){
// 4. Redis计数器控制并发
int quanty = limitPolicy.getQuanty().intValue();
if(redisTemplate.opsForValue().increment("SKU_QUANTY_"+sku_id,1) <= quanty){
// 5. 创建订单对象
Order order = new Order();
order.setOrder_id(order_id);
order.setTotal_fee(skuVo.getPrice());
order.setActual_fee(limitPolicy.getPrice());
// ... 设置其他属性
// 6. 写入消息队列
Map<String,Object> map = new HashMap<>();
map.put("order", order);
map.put("orderDetail", orderDetail);
rabbitTemplate.convertAndSend("order_queue", map);
return successResult(order_id);
} else {
return errorResult("商品已经售完了!");
}
} else {
return errorResult("活动已经过期!");
}
}
3.2.2 关键设计点
1. Redis计数器控制并发
- 使用
redisTemplate.opsForValue().increment("SKU_QUANTY_"+sku_id,1)实现原子性计数 - 确保只有指定数量的用户可以成功下单
2. 时间服务统一时间
- 通过独立的时间服务提供统一时间,避免服务器时间不一致问题
- 使用
restTemplate.getForObject("http://localhost:8000/getTime", String.class)获取时间
3. 消息队列异步处理
- 订单创建后立即返回成功,实际订单写入通过消息队列异步处理
- 提升用户体验,避免长时间等待
3.3 库存管理
3.3.1 库存扣减流程
// 支付成功后触发库存扣减
public Map<String, Object> payOrder(String order_id, String sku_id){
// 1. 更新订单状态
int flag = orderDao.updateOrderStatus(order_id);
if (flag != 1){
return errorResult("订单状态更新失败!");
}
// 2. 发送库存扣减消息
rabbitTemplate.convertAndSend("storage_queue", sku_id);
return successResult();
}
3.3.2 库存服务监听
@Component
public class StorageListener {
@RabbitListener(queues = "storage_queue")
public void handleStorageMessage(String sku_id) {
// 执行库存扣减逻辑
storageService.decreaseStock(sku_id);
}
}
4. 缓存策略
4.1 Redis缓存设计
| 缓存Key | 数据类型 | 说明 |
|---|---|---|
LIMIT_POLICY_{sku_id} | String | 秒杀政策信息 |
SKU_QUANTY_{sku_id} | String | 商品已售数量计数器 |
SKU_{sku_id} | String | 商品详细信息 |
4.2 缓存更新策略
// 政策创建时写入Redis
public void insertLimitPolicy(LimitPolicy limitPolicy) {
// 计算有效期
long diff = (end_time.getTime() - now_time.getTime())/1000;
// 写入Redis并设置过期时间
redisTemplate.opsForValue().set("LIMIT_POLICY_"+sku_id, limitPolicy, diff, TimeUnit.SECONDS);
}
// 政策到期自动删除
// Redis会自动删除过期的key
5. 消息队列应用
5.1 队列设计
| 队列名称 | 生产者 | 消费者 | 消息内容 |
|---|---|---|---|
order_queue | 订单服务 | 订单服务 | 订单信息 |
storage_queue | 订单服务 | 库存服务 | 商品ID |
5.2 消息处理流程
用户下单 → 订单服务 → order_queue → 订单服务监听 → 写入订单表
用户支付 → 订单服务 → storage_queue → 库存服务监听 → 扣减库存
6. 高并发优化策略
6.1 限流策略
- Redis计数器:使用Redis的原子性操作控制并发数量
- 时间窗口:通过秒杀政策的开始和结束时间控制访问窗口
6.2 缓存策略
- 热点数据缓存:商品信息、秒杀政策等热点数据缓存到Redis
- 计数器缓存:已售数量使用Redis计数器,避免数据库压力
6.3 异步处理
- 订单异步写入:订单创建后立即返回,实际写入通过消息队列异步处理
- 库存异步扣减:支付成功后通过消息队列异步扣减库存
6.4 数据库优化
- 读写分离:查询操作使用缓存,写操作异步处理
- 分库分表:订单表可以按时间或用户ID分表
7. 部署和启动
7.1 环境准备
- 安装JDK 1.8
- 安装Maven 3.5.2
- 安装MySQL 5.7
- 安装Redis 3.2
- 安装RabbitMQ 3.7.14
7.2 数据库初始化
# 执行SQL脚本
mysql -u root -p < seckill.sql
7.3 配置文件修改
修改各服务的application.yml文件中的数据库连接信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf8&useSSL=false
username: your_username
password: your_password
7.4 启动顺序
- 启动注册中心:
seckill_server - 启动网关服务:
seckill_zuul - 启动各业务服务:
seckill_stock、seckill_order、seckill_storage等 - 启动前端服务:
seckill_client
7.5 使用启动脚本
# Windows环境
start-all.bat
# 检查服务状态
check-status.bat
# 停止所有服务
stop-all.bat
8. 测试流程
8.1 创建秒杀政策
- 访问:
http://localhost/seckillClient/page/limitPolicyPage.html - 选择商品,设置秒杀价格、库存、开始时间、结束时间
- 点击保存
8.2 用户登录
- 访问:
http://localhost/seckillClient/page/loginPage.html - 输入用户名密码登录
8.3 商品浏览
- 商品列表:
http://localhost/seckillClient/page/stockListPage.html - 商品详情:
http://localhost/seckillClient/page/stockDetailPage.html?sku_id=xxx
8.4 秒杀下单
- 在商品详情页点击"立即抢购"
- 确认订单信息
- 提交订单
8.5 支付流程
- 点击"微信支付"
- 系统提示"支付成功"
- 库存自动扣减
9. 性能监控
9.1 关键指标
- QPS:每秒查询数
- 响应时间:接口响应时间
- 成功率:秒杀成功率
- 库存消耗速度:库存扣减速度
9.2 监控点
- Redis缓存命中率
- 消息队列积压情况
- 数据库连接池使用情况
- 各服务响应时间
10. 扩展优化建议
10.1 技术优化
- 引入分布式锁:使用Redisson实现分布式锁
- 引入限流组件:使用Sentinel或Hystrix进行限流
- 引入监控组件:使用Spring Boot Admin监控应用状态
- 引入链路追踪:使用Sleuth+Zipkin追踪请求链路
10.2 业务优化
- 引入防刷机制:限制同一用户重复下单
- 引入黑名单机制:对恶意用户进行限制
- 引入库存预热:提前将库存加载到缓存
- 引入降级机制:系统压力大时进行服务降级
10.3 架构优化
- 引入CDN:静态资源使用CDN加速
- 引入负载均衡:使用Nginx进行负载均衡
- 引入数据库集群:主从复制、读写分离
- 引入缓存集群:Redis集群提高可用性
11. 常见问题解决
11.1 秒杀超卖问题
问题:库存为负数
解决:使用Redis原子性操作控制并发
11.2 重复下单问题
问题:同一用户重复下单
解决:使用Redis记录用户下单状态
11.3 系统崩溃问题
问题:高并发导致系统崩溃
解决:使用消息队列削峰填谷
11.4 数据一致性问题
问题:缓存与数据库数据不一致
解决:使用消息队列保证最终一致性
12. 总结
本秒杀系统通过以下技术手段实现了高并发场景下的商品抢购:
- 微服务架构:服务解耦,独立部署
- Redis缓存:热点数据缓存,提升访问速度
- 消息队列:异步处理,削峰填谷
- 计数器限流:控制并发数量,防止超卖
- 时间服务:统一时间,避免时间不一致问题
系统设计充分考虑了秒杀场景的特点,通过多种技术手段保证了系统的高可用性和数据一致性。

372

被折叠的 条评论
为什么被折叠?



