在线联机原型全集:第 24 章 动态赛事系统
动态赛事系统(Dynamic Tournament System)
- 类别:实时事件驱动 + 自动调度 + 排名积分系统
- 目标:验证 Job Queue 调度、FSM 状态流转、跨时区排程与积分结算一致性
- 原型代号:
proto-024-dynamic-tournament - 依赖模块:
proto-020-lobby-portal - 推荐语言栈:Go / Java / Rust
- 协议栈:HTTP + WebSocket + JobQueue / EventBus + Redis / Kafka
- 关键主题:赛程(Schedule)、积分(Scoring)、自动调度(Auto-Scheduling)、最终一致性(Eventual Consistency)
24.1 系统目标与约束
目标定义
-
多赛制支持
- Round Robin(循环赛)
- Swiss System(瑞士制)
- Single/Double Elimination(单败 / 双败淘汰)
- League(积分联赛)
- Ladder(天梯排名制)
-
自动化赛程调度(Auto Scheduling)
- 动态时区匹配
- 跨区房间分配
- 异常重排与补赛(Reschedule / Replay)
- 延迟处理与弃权判定(No-show / Timeout)
-
积分与排名系统(Scoring & Ranking)
- Elo / Glicko-2 动态积分计算
- 积分制 + 净胜分制(Points / Goal Diff)
- 破同规则(Tiebreakers)
- 排名快照(Leaderboard Snapshot)
-
一致性保障与幂等处理
- Job Queue + 事务外发(Transactional Outbox)
- 幂等键(Idempotency Key)防重复执行
- 异常回滚与状态修复(Rollback FSM)
-
可观测性与运维
- Job 执行监控与延迟指标
- 赛程执行 SLA
- 自动预警与重试机制
24.2 领域建模(Domain Modeling)
核心实体(Entities)
| 实体 | 说明 |
|---|---|
Tournament |
赛事主档(基础信息、赛制类型、状态、配置) |
Stage |
阶段/轮次(分组结构、时间区间、依赖前置阶段) |
Bracket |
淘汰树(单败/双败结构节点) |
Match |
单场对局信息(参赛方、时间、状态、结果) |
Participant |
参赛者信息(玩家/战队、积分、历史记录) |
Standing |
积分榜(积分、净胜分、胜负比、破同规则) |
Job |
异步任务(类型、执行状态、重试策略、日志) |
ScheduleSlot |
可用资源窗口(时间、房间、服务器) |
RuleSet |
规则定义(积分、弃权、结算、处罚等) |
关系结构
Tournament1 - NStageStage1 - NMatchMatch关联 2 个ParticipantStanding从MatchResult事件流中增量更新(Event-Sourced)
关键约束
- 赛事元数据(Meta)要求强一致性(ACID)
- 赛程与积分结果支持最终一致性(Eventual Consistency)
- 任何跨分区或跨时间段任务由 Job Queue 调度触发
24.3 赛制与积分体系(Formats & Scoring System)
24.3.1 主要赛制规则
| 赛制 | 特性 | 适用场景 |
|---|---|---|
| Round Robin | 全循环对阵;总场次 = N×(N−1)/2 | 小规模正式赛事 |
| Swiss System | 胜者组对阵胜者组;轮数 ≈ log₂N | 中大型赛事 |
| Single Elimination | 一败淘汰;最短时间完成 | 淘汰赛、杯赛 |
| Double Elimination | 双败容错;上下分区 | 高水平锦标赛 |
| League / Ladder | 持续积分制;可长期维护 | 天梯、排名赛 |
24.3.2 积分与破同(Scoring & Tiebreakers)
积分规则示例:
| 结果 | 得分 |
|---|---|
| 胜 | +3 |
| 平 | +1 |
| 负 | 0 |
| 弃权 | -1 |
破同顺序示例(Tiebreak Order):
- 总积分(Total Points)
- 直接对战胜者(Head-to-Head)
- 净胜分(Point Differential)
- Buchholz 系数(Opponent Strength)
- 随机抽签或加赛(Toss/Extra Round)
动态 Elo 更新公式:
$$ E_A = \frac{1}{1 + 10^{(R_B - R_A)/400}} $$
$$ R’_A = R_A + K \times (S_A - E_A) $$
其中
- (R_A) = 当前评分
- (S_A) = 实际结果(胜=1,平=0.5,负=0)
- (E_A) = 预期结果
- (K) = 调整系数
24.4 状态机(FSM)设计
24.4.1 赛事主状态机
stateDiagram-v2
[*] --> Draft : create_tournament
Draft --> Registration : publish
Registration --> Seeding : lock_registration
Seeding --> Scheduling : build_brackets
Scheduling --> Ongoing : round_ready
Ongoing --> Paused : manual_pause / outage_detected
Paused --> Ongoing : resume
Ongoing --> Completed : all_matches_resolved
Ongoing --> Cancelled : admin_cancel
Completed --> Archived : archive
24.4.2 单场对局状态机
stateDiagram-v2
[*] --> Pending
Pending --> Scheduled : assign_slot
Scheduled --> RoomAlloc : allocate_server
RoomAlloc --> Live : start_signal
Live --> Resolving : game_over
Resolving --> Settled : commit_result
Resolving --> Disputed : dispute_open
Disputed --> Settled : arbiter_commit
Settled --> [*]
24.5 Job 调度体系(Job Scheduling System)
24.5.1 Job 类型
| 类型 | 说明 |
|---|---|
BUILD_BRACKETS |
构建分组、种子、对阵表 |
ALLOCATE_SLOTS |
分配时间与服务器资源 |
MATERIALIZE_ROUND |
实体化某轮比赛(生成房间) |
LOCK_MATCH |
锁定比赛、阻止重复启动 |
SETTLE_MATCH |
结算比赛、记录积分 |
ROLLBACK_MATCH |
回滚异常状态 |
UPDATE_STANDINGS |
更新排行榜 |
PUBLISH_LEADERBOARD |
推送积分榜 |
RESCHEDULE_MATCH |
延期/补赛任务 |
CLOSURE |
赛事冻结与归档 |
24.5.2 优先级与队列模型
critical:结算类任务(SETTLE、LOCK)sched:赛程调度类任务(ALLOCATE、BUILD)low:榜单更新类(STANDINGS、LEADERBOARD)
采用 per-key FIFO ordering:同一 Tournament 内强制串行,跨赛事并行执行。 Worker 线程数可动态扩展(例如 Go 协程池 32×N 核)。
24.5.3 幂等与事务外发
type Job struct {
ID string
Type string
Payload []byte
IdempotencyKey string
State string // pending, running, success, failed
RetryCount int
CreatedAt time.Time
}
- 幂等键规则:
tournament_id:job_type:round:match_id - 执行完成后写入 Outbox 表
- Outbox 消费器(Outbox Consumer)异步推送事件到 Kafka 或 Redis Stream
- 保证「不丢失、不重放、不并发」
24.6 数据库结构(Schema)
CREATE TABLE tournaments (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128),
format VARCHAR(32),
timezone VARCHAR(64),
state VARCHAR(16),
rule_set JSONB,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE matches (
id BIGSERIAL PRIMARY KEY,
tournament_id BIGINT,
stage_id BIGINT,
round INT,
participant_a BIGINT,
participant_b BIGINT,
scheduled_at TIMESTAMP,
state VARCHAR(16),
result JSONB,
UNIQUE(tournament_id, round, participant_a, participant_b)
);
CREATE TABLE standings (
tournament_id BIGINT,
participant_id BIGINT,
wins INT,
losses INT,
points INT,
elo FLOAT,
PRIMARY KEY (tournament_id, participant_id)
);
24.7 自动调度逻辑(Auto-Scheduling Algorithm)
Heuristic 策略
- 时区优先匹配:选手 UTC 偏移相近优先分组
- 服务器资源约束:优先分配低延迟、带宽稳定机房
- No-Show 防御:连续弃权自动标记降级(Penalty)
- 并行窗口控制:同轮最多并发 N 场,避免资源爆满
- 边缘调度补偿:调度失败重试间隔指数递增(Exponential Backoff)
调度伪代码示例
func ScheduleRound(tournamentID int64, round int) error {
matches := repo.FindPendingMatches(tournamentID, round)
for _, m := range matches {
slot := slotFinder.FindAvailableSlot(m)
if slot == nil {
queue.DelayRetry(m.ID, 10*time.Minute)
continue
}
repo.AssignSlot(m.ID, slot)
queue.Enqueue(Job{
Type: "LOCK_MATCH",
IdempotencyKey: fmt.Sprintf("%d:LOCK_MATCH:%d", tournamentID, m.ID),
})
}
return nil
}
24.8 积分结算与排行榜(Scoring & Leaderboard)
结算流程
game_over→ResultEvent→ Job:SETTLE_MATCH- 写入结果表(ResultLog)
- 更新
Standing - 推送
LeaderboardUpdated事件 - WebSocket 通知前端刷新榜单
结算事务一致性
采用 分布式事务 + Outbox 模式:
- 本地事务:更新 match → standings → 写入 outbox
- 异步消费:消息总线(Kafka / Redis Stream)处理榜单推送
- 重试策略:失败重放 ≤ 3 次,幂等验证 via
IdempotencyKey
24.9 监控与告警(Observability & Alerting)
| 指标 | 说明 |
|---|---|
job_latency_avg |
Job 平均执行延迟 |
job_retry_total |
Job 重试次数统计 |
schedule_backlog |
调度积压任务数 |
settle_fail_rate |
结算失败率 |
leaderboard_delay |
榜单更新时间延迟 |
Prometheus + Grafana 可实现 Job 实时监控,延迟超阈自动触发告警。
24.10 异常恢复与回滚(Rollback & Recovery)
-
状态不一致恢复
- 通过事件日志(Event Log)与状态快照(Snapshot)重建 FSM。
- 比较 match.state 与 leaderboard.last_update 时间差,必要时补偿。
-
重放机制(Replay Mechanism)
- Job 失败 → DelayQueue 重放
- Outbox 消息重复消费 → 幂等验证阻断重复写入
-
人工干预接口
- 管理员可触发
rollback_match或force_settle。 - 所有操作记录入审计日志(Audit Log)。
- 管理员可触发
24.11 系统部署与扩展性(Deployment & Scalability)
- 水平扩展:Job Worker 可横向扩容;同一赛事强制分区串行。
- 跨区部署:分赛事集群(Region Cluster) + 中央排行榜同步。
- Serverless 模式:每场比赛可动态创建短生命周期 RoomPod。
- 灾备策略:主赛事服 + 冷备节点;使用 CDC + WAL 增量恢复。
24.12 总结(Summary)
动态赛事系统是整个游戏生态中最复杂的异步调度核心之一。 通过 Job + FSM + Outbox 模式 的结合,可以实现:
- 精确的状态流转与容错调度;
- 跨时区公平的赛程自动分配;
- 并发安全的积分与榜单一致性;
- 可观测的任务执行链与 SLA 保障;
- 可扩展的分布式赛事平台架构。
核心理念: 「让复杂的赛事调度像流水线一样自动运转。」