1. 项目背景与核心需求
航空机票预定管理系统是民航领域最核心的业务系统之一,其本质是一个高并发、高可用的分布式事务处理系统。传统航空公司的票务系统往往采用大型主机架构,而基于SpringBoot的轻量级实现更适合中小型航空公司或作为教学演示系统。
从业务视角看,该系统需要解决三个核心问题:
- 实时库存管理:航班座位作为"瞬时库存",需要处理超卖、锁座、库存同步等典型电商问题
- 复杂业务规则:包括票价计算(基础票价+燃油附加费+税费)、退改签规则、会员折扣等
- 分布式事务:支付成功后需保证出票、库存扣减、积分累计等操作的原子性
2. 技术架构设计
2.1 整体技术栈选型
mermaid复制graph TD
A[前端] -->|Thymeleaf| B(SpringBoot)
B -->|MyBatis Plus| C[MySQL]
B -->|Redis| D[缓存层]
B -->|RabbitMQ| E[消息队列]
实际开发中我们采用以下技术组合:
- 核心框架:SpringBoot 2.7.18(LTS版本)
- ORM:MyBatis-Plus 3.5.3(简化CRUD操作)
- 数据库:MySQL 8.0(需配置事务隔离级别为READ_COMMITTED)
- 缓存:Redis 6.x(座位库存的分布式锁实现)
- 消息队列:RabbitMQ 3.11(异步处理订单状态变更)
关键提示:避免直接使用SpringBoot默认的HikariCP连接池配置,航空订票系统的数据库连接池建议配置:
properties复制spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000
2.2 领域模型设计
核心实体关系如下:
| 实体 | 关联实体 | 业务说明 |
|---|---|---|
| Flight | FlightSeat, Airport | 航班基础信息 |
| FlightSeat | Flight, Ticket | 航班座位库存状态 |
| Order | Ticket, Payment | 主订单(可含多张票) |
| Ticket | Passenger, FlightSeat | 机票实体(关联乘机人) |
| Payment | Order | 支付记录(需与第三方对接) |
java复制// 航班库存扣减的领域服务示例
public class FlightInventoryService {
@Transactional
public boolean deductSeat(Long flightId, SeatType type) {
// 使用SELECT...FOR UPDATE实现行锁
FlightSeat seat = seatMapper.selectForUpdate(flightId, type);
if (seat.getAvailable() > 0) {
seat.setAvailable(seat.getAvailable() - 1);
return seatMapper.updateById(seat) > 0;
}
return false;
}
}
3. 核心业务实现
3.1 机票预定流程
-
查询阶段(高并发读优化):
- 采用多级缓存策略:本地Caffeine缓存 → Redis集群 → 数据库
- 航班列表接口响应时间控制在200ms内
-
锁定座位(防超卖):
java复制// 基于Redis的分布式锁实现 public boolean lockSeat(String flightNo, String seatNo) { String lockKey = "lock:" + flightNo + ":" + seatNo; return redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", 10, TimeUnit.MINUTES); } -
订单创建:
- 采用TCC(Try-Confirm-Cancel)模式处理分布式事务
- 订单状态机设计:
mermaid复制stateDiagram [*] --> PENDING PENDING --> PAID: 支付成功 PENDING --> CANCELLED: 用户取消 PAID --> ISSUED: 出票完成 PAID --> REFUNDING: 发起退款
3.2 支付对接实践
航空系统常见的支付特殊性:
- 多支付方式:需要同时支持信用卡、支付宝、微信、公司月结等
- 长事务处理:国际机票支付可能涉及3D Secure验证(耗时2-5分钟)
建议采用状态机+异步通知机制:
java复制@RestController
@RequestMapping("/payment")
public class PaymentController {
@PostMapping("/callback/{channel}")
public String paymentCallback(@PathVariable String channel,
@RequestBody CallbackDTO dto) {
// 1. 验证签名
// 2. 更新订单状态
// 3. 触发后续业务(如出票)
return "success";
}
}
4. 性能优化要点
4.1 数据库层面
-
索引设计:
- 航班表必须建立复合索引:(departure_airport, arrival_airport, departure_time)
- 订单表按用户ID分片(userId % 16)
-
SQL优化:
sql复制/* 反例 - 全表扫描 */ SELECT * FROM flight WHERE DATE(departure_time) = '2023-11-15'; /* 正例 - 走索引 */ SELECT * FROM flight WHERE departure_time BETWEEN '2023-11-15 00:00:00' AND '2023-11-15 23:59:59';
4.2 缓存策略
采用多级缓存架构:
-
本地缓存:存储静态数据(机场列表、航空公司等)
java复制@Cacheable(value = "airports", key = "#code") public Airport getByCode(String code) { ... } -
分布式缓存:处理动态库存
java复制// 库存预热脚本示例 public void preloadInventory(Long flightId) { List<Seat> seats = seatMapper.selectByFlight(flightId); seats.forEach(seat -> { String key = "inventory:" + flightId + ":" + seat.getType(); redisTemplate.opsForValue().set(key, seat.getAvailable()); }); }
5. 安全防护措施
5.1 业务安全
-
防刷单:
- 同一IP/用户ID在10分钟内最多允许5个未支付订单
- 关键接口添加人机验证(如Google reCAPTCHA)
-
数据加密:
java复制// 乘机人身份证号加密存储 @Column @Convert(converter = CryptoConverter.class) private String idCardNumber;
5.2 系统安全
-
Spring Security配置:
java复制@Configuration @EnableWebSecurity public class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**").authenticated() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().permitAll(); return http.build(); } } -
接口防护:
- 启用Spring Boot Actuator的健康检查端点
- 敏感接口添加@PreAuthorize注解
- 使用Log4j2替换默认日志框架(避免日志注入)
6. 监控与运维
6.1 监控指标
核心监控项包括:
- 订单创建成功率(≥99.9%)
- 支付回调平均处理时间(≤500ms)
- 航班查询接口P99响应时间(≤800ms)
Prometheus配置示例:
yaml复制# application.yml
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
6.2 日志规范
采用结构化日志:
java复制private static final Logger logger = LogManager.getLogger();
public void createOrder(Order order) {
MDC.put("orderNo", order.getNo());
logger.info("Creating order with {} tickets", order.getItems().size());
// ...
MDC.remove("orderNo");
}
日志收集建议使用ELK Stack:
- 日志格式示例:
json复制{ "timestamp": "2023-11-15T14:23:45.123Z", "level": "INFO", "service": "order-service", "traceId": "abc123", "orderNo": "ORD202311150001", "message": "Order created successfully" }
7. 测试策略
7.1 单元测试重点
-
业务规则测试:
java复制@Test void testFareCalculation() { FareCalculator calculator = new FareCalculator(); BigDecimal fare = calculator.calculate( "PEK", "SHA", LocalDate.of(2023, 12, 25), SeatClass.ECONOMY); assertThat(fare).isEqualByComparingTo("1280.00"); } -
并发测试:
java复制@Test void testSeatLockConcurrency() throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(100); for (int i = 0; i < 100; i++) { executor.execute(() -> { try { assertThat(service.lockSeat("CA123", "15A")).isTrue(); } finally { latch.countDown(); } }); } latch.await(); assertThat(actualBookedSeats).isEqualTo(1); }
7.2 压力测试指标
使用JMeter模拟以下场景:
- 500并发用户持续查询航班
- 100并发用户同时抢购同一航班的最后10个座位
关键指标要求:
- 错误率 < 0.1%
- 平均响应时间 < 1s
- 吞吐量 > 200 TPS
8. 部署方案
8.1 容器化部署
Docker Compose示例:
yaml复制version: '3.8'
services:
app:
image: airline-booking:1.0.0
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
redis:
image: redis:6-alpine
ports:
- "6379:6379"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
8.2 高可用设计
-
应用层:
- 部署至少3个实例,通过Nginx轮询
- 启用Spring Cloud Kubernetes实现服务发现
-
数据层:
- MySQL配置主从复制
- Redis使用哨兵模式
-
灾备方案:
- 每日凌晨执行数据库全量备份
- 关键业务表设置binlog保留7天
9. 典型问题解决方案
9.1 分布式事务一致性
采用本地消息表方案:
java复制@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderMapper orderMapper;
private final MessageQueue messageQueue;
@Transactional
public void createOrder(Order order) {
// 1. 保存订单
orderMapper.insert(order);
// 2. 记录本地消息
Message msg = new Message("order_created", order.getId());
messageMapper.insert(msg);
// 3. 提交事务(订单和消息原子性保存)
}
@Scheduled(fixedDelay = 5000)
public void processPendingMessages() {
List<Message> messages = messageMapper.selectUnsent();
messages.forEach(msg -> {
if (messageQueue.send(msg)) {
messageMapper.updateStatus(msg.getId(), "SENT");
}
});
}
}
9.2 缓存与数据库一致性
采用Cache Aside Pattern策略:
- 读请求:先查缓存,命中则返回;未命中则查DB并回填缓存
- 写请求:先更新DB,再删除缓存(非更新)
java复制public Flight getFlight(String flightNo) {
String cacheKey = "flight:" + flightNo;
Flight flight = redisTemplate.get(cacheKey);
if (flight == null) {
flight = flightMapper.selectByNo(flightNo);
if (flight != null) {
redisTemplate.set(cacheKey, flight, 30, TimeUnit.MINUTES);
}
}
return flight;
}
@Transactional
public void updateFlight(Flight flight) {
flightMapper.updateById(flight);
redisTemplate.delete("flight:" + flight.getNo());
}
10. 项目演进建议
-
架构升级路径:
- 单体 → 服务拆分(订单服务、航班服务、支付服务)
- 引入Spring Cloud Alibaba生态
- 逐步迁移到云原生架构(Kubernetes+Service Mesh)
-
技术深化方向:
- 实时数据分析:Flink处理订单流水
- 智能推荐:基于用户历史订单推荐相关航班
- 多租户支持:为中小航司提供SaaS服务
-
DevOps改进:
- 搭建完整的CI/CD流水线
- 实施蓝绿部署策略
- 建立完善的监控告警体系(Prometheus+AlertManager+Grafana)
