SeatFlow 选座页:座位按可选、锁定中、已售、已选四种状态着色,底部是已选座位和合计金额

影院选座与票务

SeatFlow

面向影院的在线选座票务系统,提供场次浏览、实时选座、锁座下单与模拟支付。

  • Java 21
  • Spring Boot
  • Redis
  • RocketMQ
05 / 05 · DOCUMENTATION

验证与性能口径

用状态不变量检验交易正确性,区分历史压测、源码测试和本站 mock 验证。

正确性先于吞吐

抢座测试关心同一场次同一座位最终只有一笔有效占用,多座位冲突时不能留下部分占用。SeatConcurrencyIT 使用六十四个工作线程提交一千个调用任务;任务数不等于同时运行一千个线程。

支付、取消和对账集成测试使用 Testcontainers 提供真实依赖,检查状态推进、重复回调、Redis 自愈及五类补偿。浏览器 mock 的通过只能证明演示状态与交互,不能替代真实依赖的并发测试。

用不变量组织验证

不变量需要构造的场景观察对象
同场次同座位最多一笔有效占用多个请求争抢同一座位成功订单与 order_seat 唯一性
整批购买不能部分成功请求中混入一个已占座位其余座位与限购计数是否保持不变
订单只能从待支付推进一次支付、取消、超时相互竞争CAS 影响行数和最终状态
重复通知不产生额外业务变更同一通知重放、成功后再通知支付单、订单与座位状态
差异可以按业务依据修复删除座位图或制造孤儿状态对账前后数据及修复报告

成功请求数只描述入口结果。判断没有超卖,还要查询有效占用;判断取消真正释放,还要检查数据库和 Redis 两侧;判断幂等,也要观察限购计数是否被重复修改。每个断言都应对应一个业务不变量。

历史压测记录

项目 README 保存了 2026 年 9 月 16 日一次同机、单实例 JMeter 运行记录。本次站点建设没有重跑该压测。

指标历史值与口径
混合总请求5505 次,含浏览、注册、登录及下单
混合总吞吐351.7 QPS,不是下单接口吞吐
下单采样250 次尝试,249 成功、1 次预期座位冲突
下单延迟这 250 次采样的 P99 为 43 ms
事后唯一性检查重复场次座位占用数为 0

压测器、JVM 和数据库等依赖同机争用 CPU、内存与网络,结果适合这次环境内的相对参考,不能外推为生产容量。脚本把预期抢座冲突作为正常业务结果,因此报告的错误率零不表示全部请求都买票成功。

混合负载包含哪些请求

历史脚本有浏览和下单两类主要负载。浏览线程反复查询场次与座位图;下单线程先登录、读取最新座位图,再随机选择空闲座位提交。注册和登录也计入总请求,因此总吞吐同时包含读取、鉴权与交易处理。

脚本预建不同用户,减少同一用户频率与限购额度对抢座链路的干扰。这样的设计适合观察并发座位冲突,却不能回答某个用户持续重试时的限流体验,也不能替代缓存冷启动、数据库故障或支付回调积压的专项测试。

如何理解尾延迟

P99 43 ms 描述的是该次 250 个下单样本的尾部百分位,不是最大耗时,也不是长期稳定保证。少量样本对尾延迟的估计容易随运行波动;比较优化效果时,需要固定数据量、依赖状态、负载比例和预热方式,同时看吞吐、错误语义和资源占用。

如果只把业务冲突排除在错误率之外,却没有单独统计冲突数量,报告会掩盖用户实际买票失败的比例。当前记录同时报告 249 次成功与 1 次预期冲突,才具有可解释性。

观测哪些信号

下单计数、支付回调接受 / 重复 / 差异、各类对账修复量由 Micrometer 暴露。TraceId 帮助关联 HTTP、消息和补偿日志。判断一次“支付成功但座位未售出”应同时检查支付单、订单、有效占用和 Redis,单看接口成功率不足以定位差异。

仓库提供 Prometheus 接入,但没有提交 Grafana 预置面板,也未实现原规划的慢 SQL 拦截日志。不要把可观测性依赖已接入描述成完整生产监控已经落地。

一次差异如何排查

以“用户看到付款成功,但座位仍锁定”为例,先用订单号关联支付单与回调,确认成功通知是否被接受;再检查订单是否已支付及有效占用是否存在;最后对照 Redis 字段和对账修复量。若订单已经取消,应转向资金差异分支,不能直接强行改成已支付。

TraceId 用于串联一次执行,订单号和支付单号用于关联跨请求的业务过程。两类标识都需要:同一笔交易可能经历多次查询、支付尝试与通知,每次请求的链路标识不一定相同。

本站演示验证范围

本站状态测试覆盖整批锁座冲突、跨订单限购、支付失败后重试、重复成功、主动取消、到期释放、过期后的支付尝试、身份隔离、数据恢复、排期冲突与开售后禁止修改。

浏览器检查另外验证真实 Vue 页面中的选座、下单、支付与运营界面,以及桌面和手机布局。这里的十五分钟到期由可控时钟的状态测试验证,不需要把页面挂起十五分钟才能判定核心规则。

验证分为三层:状态测试快速检查可控规则;真实依赖集成测试检查事务、Lua、消息和 Mapper;浏览器检查真实界面是否正确表达结果。层次之间相互补充,不能把本站 mock 的一次流畅操作当成生产并发性能或真实支付安全性的证明。