淘票票项目的本地开发仓库,包含票务后端微服务、模拟支付系统、OpenResty 部署基线、已构建的前端静态资源,以及压测脚本。
本文档使用可移植写法描述本地目录约定:
- 后端仓库根目录:
<repo-root> - OpenResty 运行目录:
/usr/local/openresty/nginx - Maven 用户仓库:
$HOME/.m2/repository
项目目标是支撑演出/场次/座位的高并发抢票链路,核心关注点是:
- 秒杀入口限流与削峰
- Redis 热路径座位锁定
- 正式订单创建
- 支付状态回查
- 超时关单与取消收敛
- RocketMQ 异步最终一致性
当前锁座到支付链路已经收敛为:
seckill-service生成orderNo- Redis 仅维护座位长期状态
seat:state:*与临时锁seat:lock:* - 临时锁 owner token 统一使用
orderNo,不再单独维护lockId - Seata TCC 只覆盖
锁座 + 下单:seckill-serviceTry 写临时锁,order-serviceTry 写order_prepare - 当前已验证运行组合为
Seata client 2.0.0 + Seata server 2.6.0,不要仅因 server 升级就直接修改仓库里的 client 依赖版本 - Seat Confirm 只把长期状态推进到
seat:state=1,表示“已下单未支付”;真正支付成功后才由异步链路把它推进到seat:state=2 - TCC Confirm 时
order-service才创建正式UNPAID订单并发送TIMEOUT_CHECK延时消息 - 支付单不是锁座成功时同步创建,而是前端进入订单确认页后,由
order-service在查询订单详情时按需创建 - 支付成功或超时取消由
order-service回调seckill-service完成确认售出或释放座位 - 订单终态收敛统一走条件更新:只允许
UNPAID -> PAID / CANCELLED / TIMEOUT ORDER_PAID、TIMEOUT_CHECK、用户取消互相并发时,迟到链路只要发现订单已进入其他终态就直接跳过,不再覆盖TIMEOUT_CHECK vs 支付成功、取消 vs 支付成功、取消后迟到支付成功都已经做过实链路回放验证- Seat / Order 两个 TCC 分支都实现了空回滚防护:Seat 侧复用
seat:lock:*与lock:user:*写入短 TTLCANCELmarker,Order 侧通过order_prepare(CANCELED)拦截悬挂 Try
当前本地默认链路:
Browser -> OpenResty -> gateway -> seckill-service -> order-service -> payment-system / RocketMQ consumers
其中:
- Redis 是座位长期状态
seat:state:*、临时锁seat:lock:*与用户锁索引lock:user:*的热路径事实来源 - MySQL
taopiaopiao是正式业务数据持久化来源 - RocketMQ 承接订单超时检查、支付成功、取消收敛等异步事件
- OpenResty 同时承担前端静态资源入口、API 反向代理、秒杀闸门 Lua 限流
当前 Seata TCC 分工:
- Seat Try:校验座位长期状态必须为
0,写入seat:lock:*与lock:user:*的TRY|...|xid - Seat Confirm:删除临时锁,把
seat:state从0推进到1 - Seat Cancel:删除临时锁;若 Cancel 先到,则写入短 TTL
CANCEL|...|xidmarker 防止悬挂 - Order Try:写入
order_prepare(PREPARED),只预留建单资源,不创建正式单 - Order Confirm:创建
orders(UNPAID),发送TIMEOUT_CHECK,再把order_prepare标为CONFIRMED - Order Cancel:把
order_prepare标为CANCELED;若 Cancel 先到,则借此拦截后到的 Try
并发裁决补充:
OrderPaidConsumer只尝试执行UNPAID -> PAID;若订单已是CANCELLED / TIMEOUT / REFUNDED,则只记录告警并跳过OrderTimeoutCheckConsumer只裁决仍为UNPAID的订单;若支付已成功则推进UNPAID -> PAID并确认售出,否则推进UNPAID -> TIMEOUT并释放座位- 用户取消只允许执行
UNPAID -> CANCELLED;若支付或超时已先完成,则取消直接失败,不再回滚其他终态
本仓库不只是后端代码,还包含联调用的外围材料:
- Java 17 Maven 多模块 Spring Boot 后端
taopiaopiao-payment-system模拟支付系统deploy/openresty/OpenResty 部署基线html/前端静态页面产物bin/本地启动、停止、状态脚本conf/本地组件清单和环境变量模板
taopiaopiao/
├── bin/ # 本地启动/停止/状态脚本
├── conf/ # 本地环境配置模板与组件清单
├── deploy/openresty/ # OpenResty nginx.conf / app.conf / lua
├── html/ # 前端静态资源(admin/client/assets)
├── taopiaopiao-common* # 公共模块
├── taopiaopiao-user-service # 用户服务
├── taopiaopiao-venue-service # 场馆服务
├── taopiaopiao-event-service # 演出服务
├── taopiaopiao-session-service # 场次服务
├── taopiaopiao-seat-template-service # 座位模板服务
├── taopiaopiao-seckill-service # 秒杀/锁座服务
├── taopiaopiao-order-service # 订单服务
├── taopiaopiao-gateway # 网关服务
└── taopiaopiao-payment-system # 模拟支付系统
taopiaopiao-commontaopiaopiao-common-webtaopiaopiao-common-redistaopiaopiao-common-osstaopiaopiao-common-mq
这些模块提供统一响应、Web 公共能力、Redis Lua、OSS、RocketMQ 基础封装等共享能力。
| 服务 | 模块 | 默认端口 | 作用 |
|---|---|---|---|
| gateway | taopiaopiao-gateway |
8080 |
统一网关,承接 OpenResty 转发 |
| user-service | taopiaopiao-user-service/...-application |
8081 |
用户相关能力 |
| venue-service | taopiaopiao-venue-service/...-application |
8082 |
场馆能力 |
| event-service | taopiaopiao-event-service/...-application |
8083 |
演出能力 |
| session-service | taopiaopiao-session-service/...-application |
8084 |
场次、座位数据 |
| seat-template-service | taopiaopiao-seat-template-service/...-application |
8085 |
座位模板管理 |
| seckill-service | taopiaopiao-seckill-service/...-application |
8086 |
秒杀入口、Redis Lua 锁座、座位确认/释放 |
| order-service | taopiaopiao-order-service/...-application |
8087 |
正式订单、支付准备、超时检查、取消收敛 |
| payment-system | taopiaopiao-payment-system |
7500 |
模拟支付创建、查询、成功/失败回放 |
大部分业务服务采用三层模块结构:
*-api*-domain*-application
其中:
api放接口 DTO / Feign / 对外契约domain放领域对象、Mapper、核心业务application放 Spring Boot 启动类、配置、控制器、装配
当前仓库内已经带有前端静态资源,位于:
html/admin/html/client/html/assets/html/favicon.ico
这意味着在本机联调时,不需要额外依赖独立前端仓库即可通过 OpenResty 访问管理端和用户端页面。
OpenResty 在本项目里承担三件事:
- 前端静态资源入口
/api/请求反向代理到gateway/api/seckill/lock秒杀锁座请求的 Lua 闸门限流
- 根目录:
/usr/local/openresty/nginx - 主配置:
/usr/local/openresty/nginx/conf/nginx.conf - 路由配置:
/usr/local/openresty/nginx/conf/app.conf - Lua 目录:
/usr/local/openresty/nginx/lua/ - 静态资源目录:
/usr/local/openresty/nginx/html - 访问日志:
/usr/local/openresty/nginx/logs/tpp_access.log
仓库内对应文件:
deploy/openresty/nginx.confdeploy/openresty/app.confdeploy/openresty/lua/seckill_gate.luadeploy/openresty/lua/seckill_gate_util.luahtml/
迁移到机器时,直接同步到上述 OpenResty 目录即可。
当前 app.conf 的主要行为:
/api/seckill/lock先执行 Lua gate,再代理到http://gateway_backend/seckill/lock/api/代理到http://gateway_backend//admin/指向html/admin//client/指向html/client//assets/和/favicon.ico走静态资源/301 跳转到/admin/
taopiaopiao-payment-system/
支付系统当前已经调整为内存模式,不依赖 MySQL。
也就是说:
- 业务记录存储在
ConcurrentHashMap - Spring Boot 启动时不再初始化 Druid / DataSource / MyBatis-Plus
- 不需要创建
payment_db
当前用途是:
- 创建模拟支付单
- 查询支付状态
- 手工模拟支付成功/失败
- 配合
order-service的订单详情轮询,按需把订单推进到paymentStatus=READY - 配合订单服务完成本地联调和压测链路
- Host:
192.168.3.36 - Port:
3306 - Database:
taopiaopiao - Username:
root
说明:
- 正式业务服务当前通过 Nacos
backend-common.yaml连接taopiaopiao - 支付系统当前不再需要独立数据库
- Host:
192.168.3.36 - Port:
6349
说明:
- 主要用于秒杀热路径
- 当前实际运行形态为
NodeA(192.168.3.36)上的 Docker 单机 Redis 容器 - Redis 当前只保留座位长期状态、临时锁和用户锁索引等热路径数据;正式订单与支付状态以 MySQL 和支付系统为准
- Addr:
192.168.3.36:8848
说明:
- 本地微服务集成运行时依赖服务注册发现与配置中心
- NameServer:
192.168.3.36:9876 - 本地默认目录:
$HOME/rocketmq-all-5.4.0-bin-release
说明:
- 承接支付成功、超时检查、取消等异步消息
- 本地脚本
bin/start-rocketmq.sh默认从当前用户家目录读取 RocketMQ 安装目录,并读取ROCKETMQ_NAMESRV_ADDR
- Root:
/usr/local/openresty/nginx - Version baseline:
openresty/1.29.2.1
- Maven Home:
/usr/share/maven - 用户配置:
$HOME/.m2/settings.xml - 用户本地仓库:
$HOME/.m2/repository
当前用户级 Maven 已可独立维护镜像配置,不影响全局设置。
conf/local-components.yml:本地组件清单conf/local-env.example:环境变量模板deploy/openresty/README.md:OpenResty 部署说明SECKILL-TCC-SEQUENCE.md:锁座 + 下单 TCC 链路说明
每个应用模块的主配置一般位于:
*/src/main/resources/application.yml
重点关注:
- 数据源
- Redis
- Nacos
- RocketMQ
- 服务端口
mvn -q -DskipTests compilemvn clean install -DskipTests示例:
mvn -pl taopiaopiao-gateway spring-boot:run
mvn -pl taopiaopiao-seckill-service/taopiaopiao-seckill-service-application spring-boot:run
mvn -pl taopiaopiao-order-service/taopiaopiao-order-service-application spring-boot:run核心链路:
bash bin/start-core-services.sh全量后端:
bash bin/start-all-services.sh支付系统:
bash bin/start-payment-system.shRocketMQ:
bash bin/start-rocketmq.sh服务状态:
bash bin/status-services.sh停止服务:
bash bin/stop-all-services.sh
bash bin/stop-payment-system.sh
bash bin/stop-rocketmq.shbin/start-payment-system.sh 目前的目录解析顺序是:
PAYMENT_DIR环境变量- 当前目录下的
./taopiaopiao-payment-system - 仓库根目录下的
taopiaopiao-payment-system
因此,在当前仓库根目录执行脚本即可正常启动支付系统。
order-service 目前保留了一个仅用于并发演练的内部测试钩子:
- 接口:
POST /internal/orders/test/timeout-delay - 开关:默认关闭
- 显式开启方式:为
order-service设置tpp.test.runtime-hooks-enabled=true
该钩子只用于故障演练时人为延迟下一次 TIMEOUT_CHECK 消费,默认联调和正常运行不应开启。
建议按下面顺序启动本地联调环境:
- MySQL
- Redis
- Nacos
- RocketMQ
- OpenResty
payment-systemsession-serviceorder-serviceseckill-servicegateway- 其他辅助业务服务(如
user-service、venue-service、event-service、seat-template-service)
如果只是验证核心抢票链路,最少可启动:
- OpenResty
- payment-system
- gateway
- seckill-service
- order-service
- session-service
- MySQL
- Redis
- Nacos
- RocketMQ
这表示你运行的是旧配置或旧进程日志。
当前仓库里的支付系统已经切换为内存模式,正常情况下不需要:
payment_db- Druid 数据源
- MyBatis-Plus 持久化表
如果仍然遇到类似报错,优先检查:
- 是否拉到了最新本地代码
- 是否启动的是当前仓库中的
taopiaopiao-payment-system - 是否还存在旧的支付系统进程
logs/payment-system.log是否已经刷新为新的内存模式启动日志
优先检查:
/usr/local/openresty/nginx/conf/app.confgateway是否监听8080- OpenResty 是否已
nginx -t并 reload
当前脚本已经改为固定读取仓库自己的 .run/ 目录,因此建议直接使用仓库下的脚本,不要手写 PID 检查逻辑。
AGENTS.md:仓库协作规则SECKILL-TCC-SEQUENCE.md:锁座 + 下单 TCC 链路说明conf/README.md:本地组件配置说明conf/local-components.yml:本地组件清单conf/local-env.example:环境变量模板deploy/openresty/README.md:OpenResty 部署说明
如果后续本地目录、默认端口、启动方式、OpenResty 路由、支付系统模式再次变化,README 应优先同步更新。