1. 项目背景与核心需求
演唱会门票抢购系统是典型的高并发、分布式业务场景。当热门歌手开票时,瞬时流量可达数十万级别,这对系统的稳定性、公平性和响应速度提出了极高要求。传统单体架构在应对这类场景时往往会出现以下问题:
- 数据库连接池耗尽导致服务不可用
- 超卖或重复售卖同一座位
- 前端页面崩溃或长时间无响应
- 黄牛利用技术手段垄断票源
我们设计的分布式抢票系统采用SpringCloud微服务架构,通过以下技术手段解决上述痛点:
- 流量削峰:使用Redis集群进行库存预扣减,避免直接冲击数据库
- 公平性保障:实现基于用户ID的请求排队机制,结合验证码识别防止机器刷票
- 高可用设计:通过Nginx负载均衡和SpringCloud服务熔断保证系统可用性
- 分布式事务:采用Seata框架确保跨服务的票务数据一致性
提示:系统设计时特别考虑了移动端用户体验,将关键抢票链路响应时间控制在300ms以内,这对缓存策略和网络优化提出了很高要求。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构详解
2.1 整体架构设计
系统采用分层架构设计,各层技术选型如下:
| 层级 | 组件 | 作用 | 技术亮点 |
|---|---|---|---|
| 接入层 | Nginx + Keepalived | 负载均衡与高可用 | 动态限流配置,支持百万级QPS |
| 服务层 | SpringCloud Alibaba | 微服务治理 | Nacos服务发现 + Sentinel熔断 |
| 缓存层 | Redis Cluster | 热点数据缓存 | 自定义Lua脚本实现原子扣减 |
| 存储层 | MySQL集群 | 数据持久化 | 分库分表 + 读写分离设计 |
| 消息层 | RocketMQ | 异步解耦 | 顺序消息保障业务时序性 |
2.2 核心服务拆分
系统包含以下微服务模块:
-
用户服务:处理登录认证、权限管理
- JWT token签发与验证
- 分级权限控制(普通用户/管理员)
-
票务服务(核心):
- 场次管理(CRUD)
- 座位库存控制
- 票价策略引擎
-
订单服务:
- 购物车逻辑
- 订单状态机
- 支付超时处理
-
支付服务:
- 对接第三方支付渠道
- 交易对账
- 退款处理
-
通知服务:
- 短信/邮件推送
- 微信模板消息
- 站内信系统
3. 关键技术实现
3.1 分布式锁设计
库存扣减是系统的核心难点,我们采用Redis+Lua实现分布式锁:
java复制// 扣减库存Lua脚本
String script = "local stock = tonumber(redis.call('get', KEYS[1])) " +
"if stock > 0 then " +
" redis.call('decr', KEYS[1]) " +
" return 1 " +
"end " +
"return 0";
// 执行脚本
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("stock:"+seatId),
Collections.emptyList());
这种方案相比简单的DECR命令具有以下优势:
- 原子性执行,避免并发问题
- 单次网络IO,性能更高
- 可灵活扩展业务逻辑
3.2 秒杀流量控制
采用分级限流策略:
- 前端层:随机延迟提交(300-800ms)
- 网关层:令牌桶算法限流
- 服务层:Sentinel热点参数限流
具体配置示例:
yaml复制# Sentinel配置
spring:
cloud:
sentinel:
filter:
enabled: false
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-flow-rules
rule-type: flow
3.3 订单超时处理
使用RocketMQ延迟消息实现未支付订单自动取消:
java复制// 发送延迟消息
Message message = new Message(
"ORDER_TIMEOUT_TOPIC",
orderId.getBytes(),
"订单超时提醒".getBytes()
);
// 设置30分钟延迟
message.setDelayTimeLevel(16);
producer.send(message);
延迟级别对应时间关系:
| 级别 | 延迟时间 | 适用场景 |
|---|---|---|
| 1-3 | 1-10秒 | 快速重试 |
| 4-12 | 30秒-1小时 | 常规业务 |
| 13-18 | 1-24小时 | 长周期任务 |
4. 性能优化实践
4.1 缓存策略设计
采用多级缓存架构:
- 客户端缓存:静态资源CDN分发
- 应用缓存:Caffeine本地缓存
- 分布式缓存:Redis集群
热点数据缓存配置示例:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10000));
return cacheManager;
}
}
4.2 数据库优化
MySQL表设计遵循以下原则:
- 票务表按演出ID分片
- 订单表按用户ID哈希分库
- 建立组合索引(演出ID+座位状态)
分库分表配置示例:
yaml复制# ShardingSphere配置
spring:
shardingsphere:
datasource:
names: ds0,ds1
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
table-strategy:
inline:
sharding-column: user_id
algorithm-expression: t_order_$->{user_id % 16}
4.3 JVM调优
针对抢票场景的JVM参数设置:
code复制-server
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
-XX:+HeapDumpOnOutOfMemoryError
关键参数说明:
- G1垃圾回收器适合大内存、低延迟场景
- 最大GC停顿时间设置为200ms
- 堆内存溢出时自动生成dump文件
5. 安全防护方案
5.1 防机器人措施
- 行为验证:滑动拼图+点击验证
- 设备指纹:采集硬件特征生成唯一ID
- 请求签名:HMAC-SHA256签名验证
验证码服务接口示例:
java复制@PostMapping("/captcha/verify")
public Result verifyCaptcha(
@RequestParam String ticket,
@RequestParam String randstr) {
// 调用腾讯验证码API
CaptchaResult result = captchaService.verify(ticket, randstr);
if(!result.isSuccess()) {
throw new BusinessException("验证失败");
}
return Result.success();
}
5.2 数据安全
- 敏感信息加密:采用国密SM4算法
- 日志脱敏:自定义Logback转换器
- SQL防护:MyBatis拦截器防止注入
脱敏配置示例:
xml复制<!-- logback.xml -->
<conversionRule conversionWord="msg"
converterClass="com.ticket.mask.SensitiveDataConverter"/>
5.3 监控预警
搭建立体化监控体系:
- 基础设施层:Prometheus+Granfana
- 应用层:SkyWalking链路追踪
- 业务层:自定义埋点监控
关键监控指标:
- 接口成功率
- 平均响应时间
- Redis命中率
- 数据库连接池使用率
6. 部署与运维
6.1 容器化部署
采用Docker Compose编排服务:
yaml复制version: '3'
services:
ticket-service:
image: registry.cn-hangzhou.aliyuncs.com/ticket/ticket-service:1.0
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
deploy:
resources:
limits:
cpus: '2'
memory: 4G
6.2 CI/CD流程
GitLab Runner自动化部署流程:
- 代码提交触发单元测试
- SonarQube静态代码扫描
- 构建Docker镜像并推送到仓库
- 滚动更新生产环境Pod
6.3 灾备方案
- 同城双活:两个机房同时提供服务
- 数据同步:Canal监听MySQL binlog
- 流量切换:DNS+VIP故障转移
我在实际部署中发现,Nginx的keepalive_timeout参数对长连接性能影响很大,建议设置为:
code复制keepalive_timeout 75s;
keepalive_requests 10000;
7. 测试方案设计
7.1 压力测试
使用JMeter模拟百万级并发:
- 阶梯式加压:5000→50000用户/分钟
- 监控关键指标:
- 错误率<0.1%
- 99线<1秒
- CPU利用率<70%
测试报告关键数据:
| 场景 | QPS | 平均RT | 错误率 |
|---|---|---|---|
| 查询场次 | 12万 | 68ms | 0.02% |
| 提交订单 | 8万 | 142ms | 0.15% |
7.2 混沌工程
使用ChaosBlade模拟故障:
bash复制# 模拟网络延迟
blade create network delay --time 3000 --interface eth0
# 模拟Redis节点宕机
blade create server stop --process-cmd redis-server
7.3 全链路压测
- 影子库隔离测试数据
- 流量录制回放
- 业务指标比对
8. 典型问题解决方案
8.1 库存超卖问题
解决方案演进:
- 悲观锁:
SELECT FOR UPDATE→ 性能差 - 乐观锁:版本号控制 → 高并发下重试率高
- Redis原子操作:Lua脚本 → 最终选择方案
性能对比数据:
| 方案 | TPS | 错误率 |
|---|---|---|
| 悲观锁 | 1200 | 0% |
| 乐观锁 | 8500 | 15% |
| Redis原子操作 | 2.1万 | 0% |
8.2 分布式事务一致性问题
采用Seata的AT模式:
java复制@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {
// 扣减库存
ticketService.reduceStock(orderDTO);
// 创建订单
orderService.create(orderDTO);
// 扣减余额
accountService.debit(orderDTO.getUserId(), orderDTO.getAmount());
}
8.3 热点Key问题
针对热门演唱会场次:
- 本地缓存:Guava Cache缓存部分库存
- Key分片:
stock_{showId}_{seat%10} - 随机过期:避免缓存同时失效
9. 项目演进方向
- 智能选座算法:基于观众偏好推荐座位
- 动态定价系统:根据供需关系调整票价
- VR虚拟选座:3D可视化座位预览
- 区块链存证:票务信息上链防篡改
实际开发中我们发现,选座策略对系统性能影响很大。当实现"连座优先"算法时,Redis的ZSET结构比原生List性能提升40%:
java复制// ZSET实现连座查找
redisTemplate.opsForZSet().rangeByScore(
"seats:"+showId,
startSeat,
endSeat);
