1. 项目背景与核心需求
作为一名长期从事企业级应用开发的工程师,我最近完成了一个面向高校和中小型机构的铁路客票系统开发项目。这个系统的诞生源于一个很实际的需求痛点:每到寒暑假,高校后勤部门总要为几千名学生集中购买火车票发愁,而市面上现有的售票系统要么过于庞大(如12306),要么无法满足机构内部的灵活管理需求。
传统售票系统在私有化部署场景下主要面临三个技术挑战:
- 高并发场景下库存数据一致性难以保证(特别是退票时的库存回滚)
- 退票费率规则需要根据不同机构需求灵活调整
- 缺乏官方数据接口时的车次信息模拟问题
针对这些痛点,我们设计了一套基于SSM+Vue的轻量级解决方案。在5000并发测试中,系统实现了99.2%的订单成功率和低于500ms的退票处理延迟,这个性能指标已经能满足大多数中小型机构的实际需求。
2. 技术架构设计解析
2.1 整体技术栈选型
前端架构:
- Vue 3.2 + Element Plus构建响应式管理后台
- ECharts实现余票可视化展示
- WebSocket实现实时余票推送
- 采用JWT+Redis实现分布式会话管理
选择Vue而非React的主要考虑是:
- Element Plus组件库对管理后台开发更友好
- 国内高校技术栈多以Vue为主,便于后期维护
- 组合式API更适合处理复杂的票务业务逻辑
后端架构:
- Spring Boot 2.7 + MyBatis-Plus 3.5
- MySQL 5.7(InnoDB集群)
- Redis 6.2(分布式锁+缓存)
- RabbitMQ 3.11(异步消息)
特别说明数据库选型:虽然MySQL 8.0性能更好,但考虑到多数高校IT部门对5.7版本更熟悉,最终选择了更保守的方案。我们在表设计上做了以下优化:
sql复制CREATE TABLE `ticket_inventory` (
`id` bigint NOT NULL AUTO_INCREMENT,
`train_no` varchar(20) NOT NULL COMMENT '车次',
`depart_date` date NOT NULL COMMENT '出发日期',
`seat_type` tinyint NOT NULL COMMENT '座位类型',
`total` int NOT NULL COMMENT '总票数',
`locked` int NOT NULL DEFAULT '0' COMMENT '锁定票数',
`version` int NOT NULL DEFAULT '0' COMMENT '乐观锁版本',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_train_date_type` (`train_no`,`depart_date`,`seat_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 核心问题解决方案
2.2.1 库存一致性保障
采用三级库存控制策略:
- 前端展示层:本地缓存余票数据(60秒过期)
- 应用层:Redis集群存储实时库存
- 持久层:MySQL最终存储
关键代码示例(Redis Lua脚本):
lua复制-- 库存扣减脚本
local key = KEYS[1]
local quantity = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key) or "0")
if current >= quantity then
redis.call('DECRBY', key, quantity)
return 1 -- 成功
else
return 0 -- 库存不足
end
2.2.2 退票策略动态配置
通过Drools规则引擎实现可配置的阶梯费率,规则示例:
drl复制rule "15天前免费退"
when
$order : Order(intervalDays >= 15)
then
$order.setRefundRate(0.0);
end
rule "48小时内收5%"
when
$order : Order(intervalDays < 3 && intervalHours >= 48)
then
$order.setRefundRate(0.05);
end
3. 关键模块实现细节
3.1 车次信息管理模块
采用"静态数据+动态缓存"的混合架构:
- 基础车次信息:每天凌晨从铁路数据平台同步(或模拟生成)
- 实时余票信息:通过Redis + MySQL binlog监听同步
前端采用WebSocket实现余票实时更新:
javascript复制// 前端余票订阅
const socket = new WebSocket(`wss://${location.host}/ticket/ws`);
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if(data.type === 'INVENTORY_UPDATE') {
this.$store.commit('updateInventory', data.payload);
}
};
3.2 订单处理流程
完整的订单状态机设计:
mermaid复制stateDiagram
[*] --> PENDING
PENDING --> LOCKED: 占座成功
LOCKED --> PAYING: 发起支付
PAYING --> PAID: 支付成功
PAYING --> FAILED: 支付失败
PAID --> COMPLETED: 出票成功
LOCKED --> CANCELLED: 超时未支付
支付环节采用本地消息表保证最终一致性:
java复制@Transactional
public void processPayment(PaymentDTO dto) {
// 1. 更新订单状态
orderMapper.updateStatus(dto.getOrderNo(), OrderStatus.PAYING);
// 2. 记录支付流水(本地事务)
paymentMapper.insert(convertToPayment(dto));
// 3. 发送支付MQ消息
rabbitTemplate.convertAndSend(
"payment.exchange",
"payment.key",
buildPaymentMessage(dto)
);
}
4. 性能优化实践
4.1 高并发场景应对
通过JMeter压力测试发现的三个性能瓶颈及解决方案:
-
余票查询QPS瓶颈:
- 问题:直接查MySQL导致QPS<500
- 优化:引入多级缓存(Caffeine本地缓存+Redis集群)
- 效果:QPS提升至12000+
-
分布式锁竞争:
- 问题:Redis锁争用导致平均延迟>1s
- 优化:采用分段锁(按车次hash分片)
- 效果:延迟降低至200ms内
-
退票库存回滚:
- 问题:同步更新导致吞吐量低
- 优化:异步消息队列+批量合并更新
- 效果:吞吐量提升8倍
4.2 典型问题排查案例
问题现象:
在春节压力测试期间,出现少量订单支付成功但余票未扣减的情况。
排查过程:
- 检查本地消息表发现支付回调消息堆积
- 追踪发现RabbitMQ消费者线程被阻塞
- 进一步定位到Drools规则引擎加载耗时
解决方案:
java复制// 优化后的规则引擎初始化
@Bean
public KieContainer kieContainer() {
KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
// 预编译规则文件
Resource[] resources = resourcePatternResolver
.getResources("classpath*:rules/*.drl");
for (Resource res : resources) {
kfs.write(ResourceFactory.newClassPathResource(res.getFilename()));
}
KieBuilder kb = ks.newKieBuilder(kfs);
kb.buildAll();
return ks.newKieContainer(kb.getKieModule().getReleaseId());
}
5. 部署与运维建议
5.1 最小化部署方案
对于1000人以下的高校场景推荐配置:
- 服务器:2核4G云主机×2(1台应用+1台数据库)
- 中间件:
- Redis哨兵模式(1主1从)
- MySQL主从架构
- Tomcat连接池配置(max 200)
5.2 监控指标设置
必须监控的四个关键指标:
- 订单成功率(99%告警阈值)
- 平均响应时间(500ms阈值)
- Redis内存使用率(70%阈值)
- MySQL活跃连接数(100阈值)
Prometheus配置示例:
yaml复制scrape_configs:
- job_name: 'ticket-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app-server:8080']
- job_name: 'redis'
static_configs:
- targets: ['redis-server:9121']
6. 项目演进方向
在实际使用中,我们总结了三个值得继续优化的方向:
-
数据仿真增强:
当前采用静态数据+随机扰动的方式模拟车次信息,下一步计划引入LSTM模型生成更真实的客流波动模式。 -
多云部署支持:
为应对高校跨校区需求,正在开发基于Kubernetes的多集群部署方案,实现地域亲和调度。 -
无票预约功能:
借鉴12306候补购票机制,开发针对学生群体的智能预约排队系统,通过分析历史数据预测退票概率。
这个项目给我最深的体会是:在资源受限的中小型场景中,合适的架构折中比追求技术先进性更重要。比如我们没有直接采用分布式事务框架,而是通过"Redis+Lua+本地消息表"的组合实现了满足需求的最终一致性,大大降低了部署和维护成本。