在线联机原型全集:第 4 章 快速问答抢答(Trivia Buzzer)
类别:多人实时响应游戏原型 目标:验证服务器时钟一致性、毫秒级公平判定、广播风暴控制与题库系统。 原型代号:
proto-004-trivia-buzzer依赖模块:proto-001-echo-chatroom,proto-002-rps,proto-003-board推荐语言栈:Go / Node.js / Elixir(高并发) 网络协议:WebSocket + JSON 消息流
一、概述(Overview)
“快速问答抢答(Trivia Buzzer)” 是一种实时响应型游戏, 玩家在同一个房间中根据题目抢答,第一个答对者得分。 由于竞争发生在毫秒级时间窗口内, 该原型主要验证两项能力:
-
时间公平性(Fair Timing)
- 所有玩家的延迟不同,服务器必须作为“时间仲裁者”;
- 以服务器时钟为唯一参考,统一计算“谁先答出”。
-
高并发广播控制(Broadcast Storm Control)
- 当 N 名玩家同时答题时,消息量成倍爆发;
- 系统需要具备批量裁定、延迟抑制和并发锁控制。
除此之外,本原型还首次引入了题库系统(Question Bank)、 实时积分系统 和 反作弊机制。
二、核心玩法与系统目标(Gameplay & Objectives)
2.1 游戏玩法概述
- 玩家进入“答题房间”;
- 系统定时推送题目(含倒计时);
- 所有玩家可在时间窗口内发送答案;
- 服务器以自身时间戳排序裁定;
- 第一个正确答案的玩家获得积分;
- 轮换下一题,最终积分最高者胜。
2.2 系统功能目标
| 模块 | 功能目标 | 验证重点 |
|---|---|---|
| 时间同步系统 | 校准客户端延迟 | 服务器时钟权威机制 |
| 抢答裁定 | 多玩家并发提交 | 时间戳排序与锁判断 |
| 题库系统 | 题目随机抽取 | Redis 缓存与持久化 |
| 积分系统 | 奖励积分累积 | 实时排名 |
| 广播系统 | 批量事件发送 | 节流控制 |
| 延迟校准 | client ping offset | 网络抖动修正 |
| 防作弊 | 答案混淆与签名 | 数据加密与速率限制 |
三、系统架构设计(Architecture)
graph TD
A[Client] --> B[Gateway]
B --> C[TriviaRoomService]
C --> D[QuestionBank]
C --> E[AnswerJudge]
E --> F[ScoreService]
C --> G[BroadcastDispatcher]
模块说明
| 模块 | 职责 |
|---|---|
| Gateway | 接入层、身份验证、心跳保持 |
| TriviaRoomService | 房间管理与题目调度 |
| QuestionBank | 题目随机化与缓存管理 |
| AnswerJudge | 抢答判断与排名计算 |
| ScoreService | 积分计算与排行榜更新 |
| BroadcastDispatcher | 批量广播与消息节流 |
3.1 架构分层
-
接入层
- WebSocket + JWT 鉴权;
- Client Ping Offset 校准延迟。
-
逻辑层
- 题目广播 → 答案接收 → 排名结算 → 积分更新。
-
存储层
- Redis:题库缓存 / 排名计分;
- PostgreSQL:历史题目与玩家积分。
-
监控层
- Prometheus + Grafana 指标:平均延迟、丢包率、答题准确率。
四、功能模块详解(Functional Modules)
4.1 时间同步系统(Time Sync)
核心思想:
- 所有客户端提交答案时的时间戳以 服务器时钟 为准;
- 客户端连接时,系统执行 ping-pong 校准:
client_send_time → server_receive_time → server_send_time → client_receive_time
偏移量计算: [ offset = \frac{(server_receive - client_send) + (server_send - client_receive)}{2} ] 最终客户端以此 offset 校正本地时间。
4.2 题库系统(QuestionBank)
功能
- 提供题目获取接口;
- 支持题型:单选、多选、判断;
- 支持 Redis 缓存与批量预加载;
- 支持题目标签与难度分层。
数据结构
{
"id": 1023,
"category": "Science",
"difficulty": "Medium",
"question": "What is the boiling point of water?",
"options": ["90°C", "100°C", "110°C", "120°C"],
"answer_index": 1
}
接口
| API | 方法 | 描述 |
|---|---|---|
/question/next |
GET | 获取下一题 |
/question/batch |
GET | 批量加载题目 |
/question/report |
POST | 答案统计上传 |
4.3 TriviaRoomService(房间服务)
状态机
stateDiagram-v2
[*] --> Waiting
Waiting --> Question: 开局抽题
Question --> Answering: 开始抢答
Answering --> Judging: 超时/全部答完
Judging --> Result: 排名结算
Result --> Question: 下一题
Result --> [*]: 题库用尽
功能
- 房间成员列表管理;
- 倒计时广播;
- 控制题目推送节奏;
- 管理状态切换。
4.4 AnswerJudge(答案判定器)
核心逻辑
-
收到每个玩家的答案包:
{"uid": "A", "question_id": 1023, "answer": 1, "timestamp": 1730690000} -
校验正确性;
-
按服务器时间排序;
-
第一个正确者记分;
-
其他答对者得分递减;
-
超时或错误不计分。
并发控制
- 使用乐观锁;
- Redis Lua 脚本实现原子更新;
- 确保“只允许一个第一名”。
4.5 ScoreService(积分系统)
| 操作 | 规则 | 示例 |
|---|---|---|
| 正确第一名 | +10 分 | A 最先答对 |
| 正确但非第一 | +5 分 | B 同题答对 |
| 错误答案 | 0 分 | C 答错 |
| 超时 | -2 分 | 未答或延迟提交 |
4.6 BroadcastDispatcher(广播系统)
背景
在 1000 名玩家同时答题的场景中, 直接广播每条答案会导致 广播风暴。
解决方案
-
使用批量广播(Batch Broadcast):
- 每 200ms 聚合消息;
- 再批量发送;
-
优先发送系统关键事件(题目、结果)。
限流机制
- 每个房间限速 200 条/秒;
- 超出则丢弃非关键包。
五、流程图(Flow Diagram)
sequenceDiagram
Client->>Gateway: join_room
Gateway->>TriviaRoom: register_user
TriviaRoom->>QuestionBank: fetch_question
QuestionBank-->>TriviaRoom: send_question
TriviaRoom->>Clients: broadcast(question)
Clients->>TriviaRoom: submit(answer)
TriviaRoom->>AnswerJudge: verify
AnswerJudge->>ScoreService: update_points
TriviaRoom->>Clients: broadcast(result)
六、数据模型(Data Schema)
| 表 | 字段 | 描述 |
|---|---|---|
questions |
id, question, options, answer, difficulty | 题库表 |
answers |
user_id, question_id, answer, is_correct, latency | 答案记录 |
scores |
user_id, room_id, score, rank | 积分 |
rooms |
id, current_question, state, start_at | 房间状态表 |
七、验证指标(Metrics & KPI)
| 指标 | 目标 | 说明 |
|---|---|---|
| 平均响应延迟 | <100ms | 包含提交与广播 |
| 答题正确率 | ≥70% | 数据校验 |
| 延迟偏差 | ≤30ms | TimeSync 校准后 |
| 广播延迟 | ≤200ms | 批量广播延迟 |
| 服务器裁定一致率 | 100% | 权威时钟 |
八、扩展功能(Extensions)
-
AB Test 系统
- 不同题型、难度动态分配;
- 收集答题速度统计;
- 优化题库排序。
-
题库缓存预热
- Redis 预加载 1000 道题;
- 按类别标签索引;
- 每小时刷新。
-
反作弊机制
- 服务器签名验证题目;
- 禁止重复提交;
- 超速答题限流。
-
延迟可视化
- 前端显示延迟条;
- 动态更新 Ping 值;
- 颜色区分延迟级别。
-
排行榜与赛季制
- 每局积分累计;
- 周期性重置;
- 排名公示。
九、技术选型与实现建议
| 模块 | 技术 | 理由 |
|---|---|---|
| 房间服务 | Go + goroutine pool | 高并发定时调度 |
| 题库系统 | PostgreSQL + Redis | 可靠与高速 |
| 时间同步 | 自研 NTP 校准 / Chrony | 精确到毫秒 |
| 答题判定 | Redis Lua 原子操作 | 并发安全 |
| 广播系统 | NATS / Kafka | 支持批量异步分发 |
| 指标监控 | Prometheus + Grafana | 延迟追踪 |
| 前端 | WebSocket + React/Vue | 实时界面刷新 |
十、压测与运维(Load & Ops)
压测场景
| 场景 | 模拟行为 | 目标 |
|---|---|---|
| 高并发答题 | 1 万人同时提交 | 无判定冲突 |
| 延迟网络 | 模拟 200ms RTT | 校准误差 < 30ms |
| 广播风暴 | 每秒 1 万条消息 | 节流无丢包 |
| Redis 锁竞争 | 高频提交 | Lua 脚本无死锁 |
监控指标
answers_received_totalfirst_correct_latency_msroom_active_totalbroadcast_queue_sizetime_offset_avg
十一、设计反思与演化(Reflection & Evolution)
11.1 核心经验
- 时间校准是公平性的前提;
- “服务器时间即真理”必须是全局共识;
- 延迟差异需要在算法层抵消;
- 广播风暴控制是系统可扩展性的关键。
11.2 设计挑战
- 大量同时答题时的锁竞争;
- 时间漂移引发的判定歧义;
- 延迟恶意利用(预判提交);
- Redis 脚本执行瓶颈。
11.3 演化方向
| 能力 | 对应后续原型 |
|---|---|
| 二进制流传输与绘制 | → #5 你画我猜 |
| 时间锁步机制 | → #6 Pong |
| 并发冲突与协作 | → #9 协作扫雷 |
| 延迟同步 | → #12 占点模式 |
十二、总结
“快速问答抢答”是时间公平性的实验场。 在这里我们第一次面对“人类时间”和“服务器时间”的冲突。 这不仅是一个小型游戏原型,更是分布式系统工程的缩影:
不同的节点、不同的延迟、不同的世界, 但必须由一个权威的时钟来定义“真相”。
它让我们明白:
实时性不是速度问题,而是秩序问题。