1. 项目概述
这个基于Spring Boot的航空票务服务平台,本质上是一个典型的B2C电子商务系统在垂直领域的应用。我在2018年参与过某中型航空公司的票务系统重构,深刻理解这类系统既要处理高并发的票务查询,又要保证交易过程的原子性。现代航空票务系统早已不是简单的CRUD应用,而是融合了实时库存管理、动态定价、分布式事务等复杂技术的综合系统。
从技术架构看,项目采用了Spring Boot+MyBatis+MySQL的经典组合,这是目前Java Web开发中最成熟稳定的技术栈。Spring Boot的自动配置特性让开发者能快速搭建起具备生产级特性的Web应用,而MyBatis在复杂SQL查询方面的灵活性,正好满足票务系统多表关联查询的需求。
2. 核心需求解析
2.1 功能性需求
机票预订系统的核心业务流程可以抽象为"查询-预订-支付-出票"四个环节。每个环节都有其技术难点:
-
航班查询:需要处理海量实时数据,包括航班动态、座位库存、价格浮动等。在我的实践中,采用Redis缓存热点航线数据,查询响应时间从原来的2秒降至200毫秒内。
-
座位锁定:当用户选择航班后,系统需要临时锁定座位防止超卖。这里必须实现分布式锁,我们最终采用Redisson的RLock,配合看门狗机制避免死锁。
-
支付与出票:支付成功后的出票操作必须保证事务性。我们通过本地消息表+定时任务实现了最终一致性,异常情况下也能保证不会漏票。
2.2 非功能性需求
-
高并发处理:春运期间系统QPS可能突增至平时的10倍。我们通过:
- 接口限流(Guava RateLimiter)
- 查询结果缓存(Redis)
- 数据库读写分离
- 静态资源CDN加速
-
数据一致性:采用多版本并发控制(MVCC)处理库存超卖问题。核心SQL示例:
sql复制UPDATE flight_seats
SET status = 'LOCKED'
WHERE flight_id = ? AND seat_no = ? AND status = 'AVAILABLE'
3. 技术架构设计
3.1 系统分层架构
code复制表示层:Thymeleaf模板 + Bootstrap前端框架
业务层:Spring Boot + Spring MVC
数据层:MyBatis + MySQL + Redis
中间件:RabbitMQ(订单超时处理) + Elasticsearch(航班搜索)
3.2 数据库设计要点
机票系统的ER图核心是四张主表:
- 航班表(flight):存储航班基础信息
- 座位表(seat):记录座位状态和价格
- 订单表(order):主订单信息
- 乘客表(passenger):乘机人信息
特别注意座位表需要设计版本号字段实现乐观锁:
java复制public class Seat {
private Long id;
private String status;
private BigDecimal price;
@Version
private Integer version; // 乐观锁版本号
}
4. 关键功能实现
4.1 航班查询接口优化
原始SQL查询往往需要关联5-6张表,我们通过以下优化将响应时间控制在300ms内:
- 建立复合索引:
sql复制ALTER TABLE flight
ADD INDEX idx_departure_arrival (departure_city, arrival_city, departure_time)
- 使用MyBatis二级缓存,配置示例:
xml复制<cache eviction="LRU" flushInterval="60000" size="1024"/>
- 热点数据预加载到Redis:
java复制@Scheduled(cron = "0 0/30 * * * ?")
public void preloadHotFlights() {
// 查询未来2小时的热门航线
// 存入Redis
}
4.2 订单超时处理
采用RabbitMQ的延迟队列实现30分钟未支付订单自动取消:
- 配置延迟交换机:
java复制@Bean
public CustomExchange orderDelayExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange("order.delay.exchange", "x-delayed-message", true, false, args);
}
- 订单创建时发送延迟消息:
java复制rabbitTemplate.convertAndSend("order.delay.exchange",
"order.delay.key",
orderId,
message -> {
message.getMessageProperties()
.setDelay(30 * 60 * 1000); // 30分钟
return message;
});
5. 安全与异常处理
5.1 防重复提交
前端采用Token机制,后端使用Guava Cache记录最近操作:
java复制private Cache<String, Boolean> submitTokenCache =
CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
public boolean checkToken(String token) {
try {
return submitTokenCache.get(token, () -> true);
} catch (ExecutionException e) {
return false;
}
}
5.2 分布式事务处理
跨服务的票务支付使用Seata AT模式:
- 配置Seata代理数据源:
java复制@Bean
public DataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = properties.initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
return new DataSourceProxy(dataSource);
}
- 业务方法添加全局事务注解:
java复制@GlobalTransactional
public void completePayment(Long orderId) {
// 1. 更新订单状态
// 2. 调用库存服务扣减
// 3. 记录财务流水
}
6. 部署与监控
6.1 生产环境配置
采用Docker Compose部署:
yaml复制version: '3'
services:
app:
image: ticket-system:1.0
ports:
- "8080:8080"
depends_on:
- redis
- mysql
redis:
image: redis:6
ports:
- "6379:6379"
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
6.2 监控方案
- Spring Boot Actuator暴露健康检查:
properties复制management.endpoints.web.exposure.include=health,metrics,prometheus
management.metrics.export.prometheus.enabled=true
- Grafana监控看板配置关键指标:
- 接口响应时间P99
- MySQL活跃连接数
- Redis缓存命中率
- JVM内存使用率
7. 开发中的典型问题
7.1 日期处理陷阱
航班日期比较必须考虑时区:
java复制// 错误做法 - 直接比较LocalDateTime
if (departureTime.isBefore(LocalDateTime.now())) { ... }
// 正确做法 - 统一转换为UTC时间
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime departureUtc = departureTime.atZone(zoneId).withZoneSameInstant(ZoneOffset.UTC);
7.2 MyBatis批量插入优化
避免逐条插入导致的性能问题:
xml复制<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO passenger (order_id, name, id_card) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.orderId}, #{item.name}, #{item.idCard})
</foreach>
</insert>
8. 扩展方向建议
- 多租户支持:通过动态数据源实现航空公司多租户隔离
java复制public class TenantDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant();
}
}
- 价格计算引擎:引入规则引擎实现复杂的票价计算策略
java复制// Drools规则示例
rule "StudentDiscount"
when
$order: Order(hasStudentCert == true)
then
$order.setDiscount(0.2);
end
- 微服务改造:将系统拆分为:
- 航班服务
- 订单服务
- 支付服务
- 通知服务
采用Spring Cloud Alibaba体系,通过Nacos实现服务发现,Sentinel做流量控制。
