1. 项目背景与核心价值
海洋馆作为城市重要的科普娱乐场所,日常接待量巨大且存在明显的客流高峰波动。传统窗口售票模式在节假日经常出现排队拥堵、票务统计滞后、资源调配失衡等问题。去年夏天我在某海洋馆做技术咨询时,亲眼目睹游客在38度高温下排队两小时购票的窘境——这正是我们决定开发这套预约系统的直接动因。
这套基于SpringBoot的预约系统实现了三大核心价值:
- 客流削峰填谷:通过分时段预约机制将日接待量均匀分布
- 运营数字化:实时监控各展区人流密度,动态调整导览路线
- 服务升级:支持电子票务、智能退改、会员积分等增值服务
从技术角度看,系统需要应对几个特殊挑战:
- 瞬时高并发(开票时段可能达到5000+QPS)
- 严格的库存一致性(避免超卖展馆门票)
- 复杂的业务规则(如团体票优惠、联票组合等)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 整体架构方案
采用经典的三层架构但做了针对性强化:
code复制表现层:Vue.js + ElementUI(管理后台) + 微信小程序(C端)
业务层:SpringBoot 2.7 + Spring Security + MyBatis-Plus
数据层:MySQL 8.0(主业务) + Redis 7.0(缓存/队列)
特别设计了双写一致性保障机制:
- 库存数据在Redis采用Lua脚本原子递减
- 通过binlog监听实现MySQL与Redis的最终一致
- 引入本地消息表处理分布式事务
2.2 关键技术选型
SpringBoot优势体现:
- 内嵌Tomcat轻松应对突发流量
- Actuator端点实现完善的健康监测
- 与Sentinel天然集成实现熔断降级
特别配置示例(application.yml):
yaml复制spring:
redis:
lettuce:
pool:
max-active: 200 # 连接池优化
max-wait: 1000ms
datasource:
hikari:
maximum-pool-size: 30
connection-timeout: 30000
3. 核心业务实现
3.1 预约业务流程
完整预约链路包含11个关键步骤:
- 展馆信息预热缓存(每日凌晨执行)
- 用户身份认证(JWT + 图形验证码)
- 可售库存查询(Redis Cluster)
- 座位可视化选择(使用SVG动态渲染)
- 优惠券核销(并行校验)
- 支付预占库存(Redis分布式锁)
- 第三方支付对接(支付宝/微信)
- 支付成功回调(幂等处理)
- 电子票生成(PDF模板+二维码)
- 消息通知(RabbitMQ削峰)
- 检票入场(OCR识别)
3.2 高并发解决方案
针对开票时段的秒杀场景,我们实现了:
- 库存预热:提前加载到Redis并设置本地缓存
- 令牌桶限流:Guava RateLimiter + Nginx层限制
- 请求串行化:通过Redis队列缓冲写请求
- 热点数据分离:展馆库存按slot分片存储
关键代码片段(库存扣减Lua脚本):
lua复制local key = KEYS[1]
local num = tonumber(ARGV[1])
local remain = tonumber(redis.call('GET', key))
if remain >= num then
return redis.call('DECRBY', key, num)
else
return -1
end
4. 典型问题与解决方案
4.1 超卖问题处理
我们经历过三次线上事故才最终完善方案:
- 第一次:单纯依赖数据库行锁,导致系统崩溃
- 第二次:Redis预扣减但未处理最终一致
- 最终方案:Redis原子操作+异步对账任务
对账任务核心逻辑:
java复制@Scheduled(cron = "0 0/5 * * * ?")
public void inventoryReconciliation() {
// 查询待对账记录
List<Order> orders = orderMapper.selectUncheckedOrders();
orders.forEach(order -> {
// 对比Redis与MySQL库存差异
int delta = redisStock - dbStock;
if(delta != 0) {
// 触发自动补偿
compensationService.fixStock(delta);
}
});
}
4.2 支付掉单处理
支付回调可能因网络问题丢失,我们设计了:
- 本地任务表记录所有支付状态变更
- 定时扫描超时未支付订单(状态机控制)
- 与支付平台定期对账(每日凌晨)
状态机实现示例:
java复制public enum OrderState {
INIT,
LOCKED,
PAYING,
PAID,
CANCELLED;
private static final StateMachine<OrderState, Event> machine;
static {
machine = StateMachineBuilder.builder()
.initial(INIT)
.transition()
.from(INIT).to(LOCKED).on(Event.LOCK)
.transition()
.from(LOCKED).to(PAYING).on(Event.SUBMIT_PAY)
// 其他状态转换...
.build();
}
}
5. 系统扩展实践
5.1 动态扩容方案
通过k8s实现弹性伸缩:
bash复制# HPA配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ticket-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ticket-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
5.2 智能调度优化
基于历史数据实现:
- 使用Prophet算法预测客流
- 遗传算法优化导览路线
- 实时监控展区密度(通过WiFi探针)
预测模型核心代码:
python复制from prophet import Prophet
def predict_visitors(df):
model = Prophet(
seasonality_mode='multiplicative',
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=True
)
model.fit(df)
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
return forecast[['ds', 'yhat']]
6. 部署与监控
6.1 容器化部署
Dockerfile最佳实践:
dockerfile复制FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/*.jar app.jar
RUN bash -c 'touch /app.jar'
ENV JAVA_OPTS="-XX:+UseG1GC -Xms512m -Xmx1024m"
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
6.2 立体化监控体系
- 基础监控:Prometheus + Grafana
- 链路追踪:SkyWalking
- 日志分析:ELK Stack
- 业务监控:自定义埋点
关键监控指标:
- 库存操作平均耗时
- 支付回调成功率
- 订单状态异常率
- 预约API的P99延迟
7. 项目演进方向
当前系统已在三家海洋馆稳定运行12个月,日均处理订单3.2万笔。后续重点优化方向:
- 引入WebSocket实现实时座位图更新
- 测试MongoDB替代部分MySQL场景
- 探索使用Kafka重构消息系统
- 增加AR虚拟排队功能
特别提醒:在开发类似系统时,一定要提前与场馆运营团队深入沟通业务规则。我们曾因不了解"学生团体票需提前24小时预约"的特殊规定,导致第一版系统上线后产生大量投诉。现在回想,业务理解比技术实现更重要这个教训,价值千金。
