1. 项目背景与核心价值
民航网上订票系统是典型的B2C电子商务应用,随着国内民航客运量年均增长8.3%(IATA 2022数据),传统电话订票方式已无法满足需求。这个毕设项目采用SpringBoot框架实现全流程电子票务管理,包含航班查询、在线支付、电子登机牌等现代航空服务标配功能。我在实际开发中发现,这类系统最考验的是高并发场景下的事务一致性和系统可用性——比如当多个用户同时抢购同一航班的最后几张机票时,如何避免超卖问题。
2. 技术架构设计
2.1 分层架构实现
采用经典的三层架构:
- 表现层:Thymeleaf + Bootstrap 5实现响应式前端
- 业务层:SpringBoot 2.7 + Spring Security权限控制
- 数据层:MySQL 8.0配合Redis缓存
特别说明数据库选型:MySQL的ACID特性保障了票务交易的一致性,而Redis的原子操作(如DECR)能有效处理库存扣减。实测在4核8G服务器上,这种组合可支撑每秒300+的订票请求。
2.2 核心业务流程
- 航班动态查询:使用Quartz定时同步民航局公开数据接口
- 座位锁定机制:Redis SETNX实现分布式锁(关键代码示例)
public boolean lockSeat(String flightNo) { return redisTemplate.opsForValue() .setIfAbsent("lock:"+flightNo, "1", 30, TimeUnit.SECONDS); }- 支付回调处理:采用支付宝沙箱接口,注意验签和幂等性设计
3. 关键问题解决方案
3.1 超卖问题处理
对比了三种方案后选择Redis+Lua脚本方案:
- 纯数据库方案:UPDATE语句带库存校验,性能差(TPS<50)
- 乐观锁方案:版本号控制,用户体验不佳
- Redis原子操作:Lua脚本保证原子性,TPS可达1200+
库存扣减Lua脚本示例:
local stock = tonumber(redis.call('GET', KEYS[1])) if stock > 0 then redis.call('DECR', KEYS[1]) return 1 end return 03.2 分布式事务挑战
机票预订涉及多个子系统(订单、支付、短信),采用本地消息表+定时任务补偿机制。重要教训:短信发送等非核心业务必须异步化,否则会成为系统瓶颈。
4. 开发实战经验
4.1 航班数据获取
民航局公开接口存在三个坑:
- 响应时间不稳定(200ms-2s波动)
- 数据格式不统一(同一字段有时是字符串有时是数字)
- 没有历史数据接口
解决方案:建立本地航班库,用Spring Batch每日全量同步+增量更新。
4.2 支付对接注意事项
- 支付宝沙箱环境经常返回异常状态(特别是晚上10点后)
- 必须记录原始请求和响应报文
- 支付结果查询要做延迟处理(实测约15%回调会延迟3-5分钟)
5. 性能优化记录
5.1 缓存策略优化
经过JMeter压测发现:
- 航班查询接口响应时间从800ms降至120ms
- 关键配置:Spring Cache的@Cacheable需要指定cacheNames
@Cacheable(value = "flightCache", key = "#departureDate") public List<Flight> getFlights(LocalDate departureDate) {...}5.2 数据库优化
- 航班表建立复合索引(departure_city, arrival_city, departure_time)
- 订单表做水平分表(按用户ID哈希)
- 使用Flyway管理数据库变更
6. 典型问题排查
6.1 重复订票问题
现象:同一用户短时间内生成多个待支付订单 根因:前端防重提交失效+后端未做请求幂等 解决方案:采用Token机制+Redis原子计数器
6.2 支付状态不同步
问题定位过程:
- 检查本地事务日志
- 核对支付宝交易号
- 验证签名算法 最终发现是证书过期导致验签失败
7. 扩展功能建议
- 智能推荐:基于用户历史订单推荐关联航线(可用Mahout实现)
- 候补购票:Redis ZSET实现排队机制
- 行程管理:iCalendar协议生成行程日历
这个项目让我深刻体会到,看似简单的订票业务背后需要处理各种边界情况。比如遇到航班变动时,如何批量处理已出票订单的改签通知?实际开发中我们最终采用了事件驱动架构,通过Spring ApplicationEvent实现解耦。