1. 项目背景与核心需求
航空票务系统作为现代旅游业的基础设施,其技术实现一直面临着高并发、实时性和复杂业务规则的挑战。我去年参与重构的某中型航空公司售票系统,日均处理订单量峰值达到12万笔,系统响应时间要求控制在800毫秒以内。这种量级的业务压力下,传统的Servlet+JSP架构已难以胜任,这正是我们选择SpringBoot作为技术栈的根本原因。
SpringBoot的自动配置特性让我们在两周内就搭建起了具备基本功能的原型系统。机票预订业务看似简单,实则包含航班查询、座位锁定、支付处理、出票通知等17个核心子模块。其中最难处理的是"幽灵座位"问题——当多个用户同时查询同一航班时,如何避免超售?我们最终采用Redis分布式锁+数据库乐观锁的双重机制,将超售率控制在0.003%以下。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型决策
后端采用SpringBoot 2.7 + MyBatis-Plus组合,前者提供开箱即用的Web MVC和事务管理支持,后者简化了90%的CRUD操作。数据库选用MySQL 8.0配合ShardingSphere实现分库分表,将订单表按用户ID哈希分散到8个物理节点。这里有个关键细节:航班基础信息需要全量缓存,我们对比了Redis和Memcached后,最终选择Redis Cluster,主要看中其原生支持的Lua脚本特性,可以原子化执行座位库存扣减。
前端采用Vue3+Element Plus,通过Nginx实现动静分离。特别要说明的是支付模块的隔离设计——我们将支付相关接口单独部署在金融区VPC,与主业务区通过专线连接,这样即使Web层被攻破,支付核心链路仍能保持安全。
2.2 微服务拆分策略
虽然SpringBoot支持单体应用开发,但考虑到机票系统的业务复杂度,我们将其拆分为:
- 航班服务(含时刻表、机型数据)
- 订单服务(处理预订流程)
- 支付服务(对接第三方支付渠道)
- 通知服务(短信/邮件提醒)
每个服务都有独立的SpringBoot应用,通过Nacos实现服务发现。这里有个实际教训:最初我们将航班搜索和订单处理放在同一个服务,结果QPS达到3000时出现线程池耗尽,后来通过拆分使单服务吞吐量提升了4倍。
3. 核心业务流程实现
3.1 航班查询优化
航班搜索接口的响应速度直接影响用户体验。我们通过多级缓存实现毫秒级响应:
- 第一层:本地Caffeine缓存,有效期5分钟
- 第二层:Redis集群缓存,有效期2小时
- 第三层:数据库查询,仅当缓存未命中时触发
查询参数处理也有讲究:将出发地、目的地、日期组合成"PEK-SHA-20230815"这样的缓存键,配合布隆过滤器防止缓存穿透。实测下来,99.2%的查询请求能在3毫秒内返回结果。
3.2 订单创建流程
机票订单创建是个典型的长事务过程,我们采用状态机模式管理订单生命周期:
java复制// 订单状态枚举设计
public enum OrderStatus {
INIT(1),
LOCKED(2),
PAYING(3),
PAID(4),
ISSUED(5),
CANCELLED(-1);
// 状态转换校验逻辑
public boolean canTransferTo(OrderStatus next) {
// 具体实现省略...
}
}
关键点在于座位锁定的实现:先通过Redis执行原子化的座位数扣减,如果成功才创建数据库订单记录。这里有个容易踩的坑——Redis锁必须设置合理的过期时间(我们设为15分钟),否则系统崩溃会导致座位长期不可售。
4. 高并发处理方案
4.1 库存控制机制
机票库存不同于普通商品,具有强烈的时效性和不可替代性。我们的解决方案是:
- 实时库存:Redis Hash存储每个航班的可售座位数
- 预扣库存:创建订单时先扣减Redis库存
- 最终库存:支付成功后同步到数据库
通过定时任务每5分钟对账Redis和数据库的库存差异。曾遇到Redis集群脑裂导致库存不一致的问题,后来引入Redisson的看门狗机制解决了这个问题。
4.2 分布式事务处理
跨服务的订单-支付流程需要事务保障,我们对比了Seata和本地消息表方案后,选择后者实现最终一致性。核心逻辑是:
- 订单服务创建订单并发送MQ消息
- 支付服务消费消息并处理支付
- 若支付成功则更新订单状态,失败则触发补偿流程
这里要特别注意消息幂等处理:我们为每条消息附加了业务唯一ID,并在支付服务端维护了去重表。
5. 安全防护措施
5.1 防刷单策略
机票系统常遭遇黄牛刷单,我们实施了多维度防护:
- 用户行为分析:识别异常查询模式(如高频查询同一航班)
- 验证码挑战:当疑似机器人访问时触发
- 限流措施:Guava RateLimiter控制接口调用频次
特别有效的是基于用户画像的动态规则:对于新注册账号且立即查询热门航线的行为,自动提升风控等级。
5.2 敏感数据保护
乘客身份证号、手机号等数据严格加密存储:
- 数据库层面使用AES-256加密
- 日志输出时自动脱敏
- 内部系统间传输采用SSL加密
曾因开发人员误将日志级别设为DEBUG导致敏感信息泄露,后来我们通过自定义Logback过滤器彻底解决了这个问题。
6. 监控与运维实践
6.1 全链路监控
基于SpringBoot Actuator搭建的监控体系包含:
- Prometheus采集JVM指标
- Grafana展示业务大盘
- ELK收集业务日志
特别有价值的是自定义的"关键业务指标看板",实时显示:
- 订单创建成功率
- 平均响应时间
- 支付转化率
6.2 灰度发布方案
采用Kubernetes的Deployment滚动更新策略,配合Nginx流量切分实现无损发布。我们的经验是:先发布1个Pod观察5分钟,确认无异常后再全量发布。曾因未充分测试的新版本导致订单服务宕机,后来强制实施"预发布环境全流程验证"制度。
7. 性能优化实战
7.1 JVM调优
针对机票查询的高并发特点,我们对SpringBoot应用的JVM参数做了特别优化:
- 使用G1垃圾回收器
- 初始堆内存设为4G(物理内存的1/4)
- 开启-XX:+UseStringDeduplication减少内存占用
通过JMeter压测,优化后GC停顿时间从原来的1.2秒降低到200毫秒以内。
7.2 SQL优化案例
航班搜索涉及多表关联查询,我们通过以下手段提升性能:
- 为常用查询条件建立组合索引
- 将文本搜索改用ES实现
- 对大表进行水平分片
有个实际案例:航线热度统计查询原本需要8秒,通过添加覆盖索引和查询重写,最终优化到300毫秒。
8. 扩展性设计
8.1 多租户支持
为后续接入旅行社预留了多租户架构:
- 数据库层面采用schema隔离
- Spring Security实现租户鉴权
- 动态数据源切换
关键点在于ThreadLocal保存租户上下文,确保线程内所有操作都路由到正确库。
8.2 国际机票扩展
系统初期只支持国内机票,我们在设计时预留了:
- 多语言支持(i18n)
- 多币种结算
- 时区敏感的时间处理
后来接入国际航司时,这些预先考虑节省了60%的开发工作量。
9. 踩坑与解决方案
9.1 缓存雪崩事件
某次大促销期间,大量缓存同时过期导致数据库瞬时压力激增。我们通过以下方法解决:
- 给缓存过期时间添加随机抖动(±10%)
- 实现缓存预热机制
- 增加数据库连接池备用连接
9.2 第三方接口超时
支付网关不稳定会导致订单状态不一致,我们引入:
- Hystrix熔断机制
- 异步重试策略
- 人工干预后台
现在回想起来,最大的教训是:任何外部依赖都必须有超时设置和降级方案。
10. 项目演进方向
当前系统已稳定运行2年,接下来的优化重点包括:
- 引入React Native重构移动端
- 试用SpringCloud Alibaba升级微服务架构
- 基于Flink实现实时风控
特别期待Java 21的虚拟线程特性,可能带来新一轮性能提升。不过架构升级需要谨慎,我们的原则是:不追求最新技术,只采用最适合业务场景的方案。
