webman适合作为测评系统底层框架,但实时功能需交由workerman websocket处理;试卷生成应预热缓存+动态难度+布隆过滤;成绩计算须用整数存储与bc高精度运算;高并发提交必须队列+分表。

Webman 本身不直接提供测评系统功能,但它是构建高性能在线教育测评系统的极佳底层框架——关键在于把“测验逻辑”和“高并发交互”拆开:用 Webman 处理试卷生成、答题提交、成绩计算等有状态、需事务的 HTTP 请求;把实时监考、倒计时同步、多人同场作答等毫秒级响应需求交给独立的 Workerman WebSocket 服务。
为什么不能把倒计时和防切屏逻辑塞进 Webman 路由
Webman 是 HTTP 框架,基于短连接;而倒计时需持续心跳、防切屏要监听浏览器 visibilitychange 并实时上报,这些必须走长连接。硬塞进 Route::post('/submit') 会导致:用户交卷前切屏 3 次,后端只收到 1 次请求,无法取证;前端每秒轮询 /api/v1/heartbeat,QPS 翻 10 倍,Webman 进程 CPU 瞬间拉满。
- 正确做法:前端连
ws://exam.your-domain.com:2346(Workerman 启动的独立 WebSocket 服务) - WebSocket 服务在
onMessage中只做两件事:写 Redis(如SET exam:session:{token}:focus_count 5),发消息到指定 room - Webman 的
ExamSubmitController@handle只负责校验答案、写 MySQL、触发成绩统计任务,不碰任何实时状态
试卷生成与题型适配的关键参数控制
动态组卷不是拼 SQL,而是靠策略+缓存。常见错误是每次请求都查库生成整套题,导致 500 人同时抽卷时数据库慢查询飙升。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 题库预热:用
php think queue:work --daemon启动队列,凌晨把高频试卷(如“初中数学期中A卷”)生成并存入 Redis Hash,key 为paper:template:math_midterm_a - 难度浮动:调用
QuestionPool::fetchByDifficulty($level, $count, ['type' => 'single_choice']),其中$level不是固定值,而是根据当前用户历史得分动态计算(例如上次平均分 72 → 本次$level = 3.2) - 防重复:对已抽过的题 ID 做布隆过滤器(
BloomFilter::add('exam:user:12345', $question_id)),避免同一用户 24 小时内重复见到相同题干
成绩计算必须绕开 PHP 的浮点数陷阱
选择题判分看似简单,但 0.1 + 0.2 == 0.3 在 PHP 里是 false,直接用于百分制换算会引发成绩误差。尤其当系统支持小数权重(如单选题 1.5 分、填空题 2.3 分)时更危险。
- 所有分值存储统一用整数(单位:十分之一分),例如 85.5 分存为
855,数据库字段类型为INT - 计算逻辑封装在
ScoreCalculator::calculate($raw_scores),内部用bcmul()和bcadd()做高精度运算 - 前端展示时再除以 10:
number_format($score / 10, 1),避免 JS 浮点问题二次干扰
MySQL 写压力大的地方必须加队列和分表
一场万人考试结束瞬间,所有考生同时提交,INSERT INTO exam_records 会成为性能瓶颈。别指望加索引或调 innodb_buffer_pool_size 解决根本问题。
- 答题记录先入 Kafka 或 Redis List,Webman 接口只返回
{"status": "accepted", "task_id": "exam_20260729_abc123"} - 后台消费进程(
php think queue:work)按考场 ID 分片处理,每个进程只写对应分表,如exam_records_001、exam_records_002 - 分表键必须是
exam_id(非user_id),因为查某场考试整体数据比查某人所有考试更频繁
真正卡住多数人的不是技术选型,而是没想清楚“哪些必须实时、哪些可以异步”。比如防作弊截图上传,与其要求前端立即压缩+上传+校验,不如允许离线缓存 30 秒,通过 WebSocket 回传 base64 片段再拼接——这能降低 70% 的瞬时带宽峰值。










