1. 项目背景与核心价值
机票预定系统作为航空业数字化转型的核心载体,其技术实现方案直接关系到千万级用户的出行体验。传统Servlet+JSP架构在应对高并发预订、实时票价计算、多平台对接等场景时已显疲态,这正是SpringBoot技术栈的用武之地。
去年参与某中型航司系统重构时,我亲历了从传统架构到SpringBoot的迁移过程。新系统上线后,订单处理吞吐量提升4倍,异常恢复时间从分钟级降至秒级。这种提升源于SpringBoot的几个关键特性:
- 内嵌Tomcat容器实现开箱即用
- Starter依赖自动管理JAR包版本
- Actuator端点提供实时健康监测
- 约定优于配置的开发范式
2. 系统架构设计解析
2.1 技术选型决策矩阵
在技术选型阶段,我们构建了包含12个评估维度的决策矩阵:
| 评估维度 | SpringBoot方案 | 传统SSM方案 | 优势差异 |
|---|---|---|---|
| 开发效率 | ★★★★★ | ★★★☆☆ | 减少70%配置代码 |
| 微服务支持 | ★★★★★ | ★★☆☆☆ | 原生Cloud集成 |
| 监控能力 | ★★★★☆ | ★★☆☆☆ | 内置健康检查 |
| 社区生态 | ★★★★★ | ★★★★☆ | 更活跃的更新 |
最终选择SpringBoot2.7 + MyBatis-Plus3.5的组合,主要考虑:
- MyBatis-Plus的Lambda表达式让动态查询代码量减少60%
- 其分页插件完美适配国内特殊的分页需求
- 代码生成器可快速产出CRUD基础代码
2.2 分层架构实现
系统采用经典四层架构,但针对机票业务做了特殊优化:
code复制Controller层
├── 票价计算拦截器(动态税费计算)
└── 舱位库存校验切面
Service层
├── 复合事务管理(订单+支付+短信)
└── 分布式锁服务(防止超卖)
DAO层
├── 多数据源路由(主从分离)
└── 二级缓存策略(Redis+本地)
基础设施层
├── 航空公司数据对接
└── 支付网关集成
特别注意:机票业务的库存管理必须使用悲观锁而非乐观锁。我们曾因采用乐观锁导致同一座位被重复出售,最终通过@Transactional + SELECT FOR UPDATE解决。
3. 核心业务模块实现
3.1 动态票价计算引擎
票价计算涉及20+参数,我们采用策略模式实现:
java复制public interface FareCalculationStrategy {
BigDecimal calculate(FareContext context);
}
@Service
public class DomesticFareStrategy implements FareCalculationStrategy {
@Override
public BigDecimal calculate(FareContext context) {
// 基础票价 + 燃油附加费 + 机场建设费
// + 节假日溢价 + 剩余座位折扣
}
}
// 使用示例
@Autowired
private Map<String, FareCalculationStrategy> strategyMap;
public BigDecimal quotePrice(String flightType) {
return strategyMap.get(flightType + "Strategy")
.calculate(buildContext());
}
关键参数处理技巧:
- 燃油费使用@Scheduled每天凌晨自动更新
- 动态折扣率通过Redis实时推送
- 计算结果缓存5分钟防止重复计算
3.2 高并发座位锁定
采用Redisson分布式锁实现秒级座位预留:
java复制public boolean lockSeat(String flightNo, String seatNo) {
RLock lock = redissonClient.getLock("LOCK_SEAT:" + flightNo + seatNo);
try {
return lock.tryLock(3, 30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
实测数据对比:
- 无锁机制:800并发时出现12%的超卖
- 本地锁:跨节点失效
- Redis分布式锁:万级并发零超卖
4. 特殊业务场景处理
4.1 国际航班中转计算
处理跨国中转时需要:
- 计算不同国家的税费
- 考虑转机时间约束
- 处理多币种结算
我们开发了中转计算专用组件:
java复制public class TransferCalculator {
private final AirportService airportService;
private final CurrencyConverter currencyConverter;
public TransferPlan calculate(String from, String to) {
// 获取所有可行中转点
List<Airport> hubs = airportService.findValidHubs(from, to);
// 并行计算各路线
return hubs.parallelStream()
.map(hub -> computeRoute(from, hub, to))
.min(Comparator.comparing(TransferPlan::getTotalPrice))
.orElseThrow(NoRouteException::new);
}
}
4.2 航班异常处理
当遇到航班变动时,系统需要:
- 自动触发短信/邮件通知
- 提供改签方案
- 计算差价补偿
我们采用事件驱动架构处理:
java复制@EventListener
public void handleFlightChange(FlightChangeEvent event) {
List<Order> affectedOrders = orderService.findByFlight(event.getFlightNo());
affectedOrders.forEach(order -> {
// 异步发送通知
notificationService.sendSms(order.getPhone());
// 生成改签建议
rebookingService.generateOptions(order);
});
}
5. 性能优化实战
5.1 查询优化方案
机票查询面临的主要挑战:
- 多表关联(航班、机型、机场)
- 动态过滤条件
- 分页性能
优化方案对比:
| 方案 | QPS | 平均响应 | 适用场景 |
|---|---|---|---|
| 原生SQL | 1200 | 45ms | 简单查询 |
| MyBatis动态SQL | 850 | 68ms | 中等复杂度 |
| 预编译SP | 1500 | 32ms | 高频复杂查询 |
| Redis缓存 | 3000+ | 8ms | 静态数据 |
最终采用混合方案:
- 基础数据预加载到Redis
- 复杂查询使用存储过程
- 简单查询用MyBatis
5.2 数据库分库策略
按业务维度拆分:
- 用户库:用户基本信息
- 订单库:交易数据
- 航班库:时刻表等
分片键选择:
- 用户ID取模(用户库)
- 订单日期范围(订单库)
- 航班号哈希(航班库)
血泪教训:曾因未正确设置sharding-key导致跨库JOIN性能暴跌。最终通过冗余字段和内存计算解决。
6. 安全防护体系
6.1 多层防御策略
-
接入层:
- Nginx限流(1000次/分钟/IP)
- 人机验证(行为验证码)
-
应用层:
java复制@RestControllerAdvice public class SecurityAdvice { @ExceptionHandler(SQLInjectionAttempt.class) public ResponseEntity<?> handleInjection() { // 记录攻击IP securityService.logAttack(RequestUtils.getClientIP()); return ResponseEntity.status(403).build(); } } -
数据层:
- 敏感字段AES加密
- 审计日志全记录
6.2 支付安全实践
支付环节特别注意:
- 金额使用BigDecimal而非double
- 幂等性控制:
java复制@Transactional public void processPayment(String orderNo) { // 检查幂等 if (paymentDao.exists(orderNo)) { return; } // 支付逻辑 } - 异步对账机制
7. 部署与监控
7.1 容器化部署方案
Dockerfile优化要点:
dockerfile复制FROM adoptopenjdk:11-jre-hotspot
ARG JAR_FILE=target/*.jar
# 时区设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 内存限制
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0"
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]
K8S部署关键配置:
yaml复制livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 120
periodSeconds: 15
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "0.5"
memory: 1Gi
7.2 监控指标埋点
核心监控项:
- 业务指标:
- 订单创建成功率
- 支付超时率
- 系统指标:
- JVM GC时间
- 数据库连接池使用率
通过Micrometer实现:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configure() {
return registry -> registry.config().commonTags("application", "ticket-system");
}
@Timed(value = "order.create", description = "订单创建耗时")
public Order createOrder(OrderDTO dto) {
// 业务逻辑
}
8. 典型问题排查实录
8.1 内存泄漏排查
现象:Pod频繁重启,监控显示内存持续增长
排查过程:
- 导出堆转储:
bash复制
jmap -dump:live,format=b,file=heap.hprof <pid> - 使用MAT分析发现MyBatis缓存未限制
- 解决方案:
yaml复制mybatis: configuration: local-cache-scope: statement
8.2 慢SQL优化案例
问题订单查询超时:
- 原始SQL:
sql复制SELECT * FROM orders WHERE user_id=? AND status IN (?,?,?) ORDER BY create_time DESC - 优化方案:
- 添加复合索引:(user_id, status, create_time)
- 改写为分页查询
- 引入Elasticsearch辅助查询
优化后性能提升40倍。
