资讯动态

自建在线编程笔试平台:Node.js+Vue+MySQL技术栈实践

发布时间:2026/8/22 8:45:33 来源:尧图企业网站定制
1. 为什么需要自建在线编程笔试平台在技术招聘领域编程笔试一直是筛选候选人的重要环节。传统的线下笔试存在场地限制、监考成本高、阅卷效率低等问题。而第三方在线编程平台往往收费昂贵且无法根据企业技术栈进行深度定制。我曾在某互联网公司负责技术招聘每年校招季都要处理上千份笔试答卷。最初我们使用商业化的在线编程平台但遇到了几个痛点一是无法与企业内部的面试系统打通二是编程环境与公司实际技术栈差异较大三是核心数据存储在第三方存在安全隐患。基于这些需求我们决定自研一套轻量级的在线编程笔试系统。经过技术选型最终采用Node.jsVueMySQL的技术栈实现。这个方案的优势在于前后端分离架构便于扩展和维护Node.js高并发特性适合笔试场景的流量峰值Vue的组件化开发能快速构建交互式编程界面MySQL成熟稳定适合存储笔试题目和答卷数据2. 系统架构设计与技术选型2.1 整体架构设计系统采用经典的三层架构前端展示层(Vue) ←→ 业务逻辑层(Node.js) ←→ 数据存储层(MySQL) ↑ (WebSocket) ↓ 代码执行沙箱环境前端使用Vue 3 Element Plus构建管理后台和考生界面通过Axios与后端通信。对于需要实时更新的功能如代码执行结果、考试倒计时采用WebSocket保持长连接。后端基于Express框架搭建RESTful API核心模块包括用户认证模块JWT题目管理模块考试管理模块代码执行模块防作弊监控模块数据库使用MySQL 8.0主要表结构设计users用户信息problems编程题目exams考试信息submissions代码提交记录exam_records考试记录2.2 关键技术选型解析代码执行沙箱方案对比我们评估了三种方案直接使用Node.js的child_process优点实现简单缺点安全性差容易造成系统崩溃Docker容器方案优点隔离性好缺点启动开销大约1-2秒轻量级沙箱如isolate优点毫秒级启动缺点配置复杂最终选择Docker方案虽然启动稍慢但安全性更有保障。通过预启动容器池的方式优化响应时间。实时通信方案对比了Socket.io和原生WebSocketSocket.io功能丰富但包体积大原生WebSocket更轻量但需自行处理断线重连考虑到笔试场景不需要复杂功能选择了原生WebSocket实现。3. 核心功能实现细节3.1 在线代码编辑器集成采用Monaco EditorVS Code同款编辑器作为代码编辑核心关键配置import * as monaco from monaco-editor this.editor monaco.editor.create(document.getElementById(editor), { value: initialCode, language: javascript, theme: vs-dark, automaticLayout: true, minimap: { enabled: false }, fontSize: 14, scrollBeyondLastLine: false })需要特别注意通过webpack插件配置Monaco Editor的按需加载不同编程语言需要单独注册语言支持实现代码自动补全需要配置对应的语言服务3.2 代码执行与结果返回代码执行流程前端将用户代码测试用例发送到后端后端生成随机容器名创建临时Docker容器在容器内执行代码并捕获输出销毁容器返回执行结果关键Node.js代码const { Docker } require(dockerode) const docker new Docker() async function runCode(language, code, testCases) { const container await docker.createContainer({ Image: code-runner-${language}, Cmd: [/bin/bash, -c, echo ${code} main.js node main.js], Tty: false }) await container.start() const stream await container.logs({ follow: false, stdout: true, stderr: true }) let output stream.on(data, chunk output chunk) await new Promise(resolve stream.on(end, resolve)) await container.remove() return output }3.3 防作弊机制实现我们实现了多层次的防作弊方案界面锁定全屏API检测窗口失去焦点警告禁止右键和开发者工具document.addEventListener(fullscreenchange, () { if (!document.fullscreenElement) { warnUser(请返回全屏模式否则可能被判定为作弊) } }) document.addEventListener(keydown, (e) { if (e.key F12 || (e.ctrlKey e.shiftKey e.key I)) { e.preventDefault() recordSuspiciousActivity(尝试打开开发者工具) } })行为监控键盘输入频率分析代码相似度检测异常复制粘贴行为记录环境检测多终端登录检测IP地址变化监控屏幕共享软件检测4. 数据库设计与性能优化4.1 核心表结构设计题目表(problems)CREATE TABLE problems ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT NOT NULL, difficulty ENUM(easy, medium, hard) NOT NULL, time_limit INT COMMENT 毫秒, memory_limit INT COMMENT MB, template_code TEXT, test_cases JSON, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )考试记录表(exam_records)CREATE TABLE exam_records ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, exam_id INT NOT NULL, start_time DATETIME NOT NULL, submit_time DATETIME, ip_address VARCHAR(45), device_info VARCHAR(255), FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (exam_id) REFERENCES exams(id), INDEX idx_user_exam (user_id, exam_id) )4.2 性能优化实践查询优化为常用查询条件添加复合索引大文本字段如代码使用TEXT类型并单独存储分页查询使用游标而非OFFSET缓存策略题目内容使用Redis缓存考试结果预生成静态报告热点数据内存缓存批量插入优化 处理大规模笔试结果时// 低效方式 for (const record of records) { await db.query(INSERT INTO submissions SET ?, record) } // 优化方式 const values records.map(r [r.user_id, r.problem_id, r.code]) await db.query( INSERT INTO submissions (user_id, problem_id, code) VALUES ?, [values] )5. 部署与运维实践5.1 容器化部署方案使用Docker Compose编排服务version: 3 services: web: build: ./web ports: - 3000:3000 depends_on: - redis - mysql api: build: ./api environment: - DB_HOSTmysql - REDIS_HOSTredis ports: - 4000:4000 mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORDsecret - MYSQL_DATABASEcode_test volumes: - mysql_data:/var/lib/mysql redis: image: redis:alpine ports: - 6379:6379 volumes: mysql_data:5.2 监控与日志收集关键监控指标API响应时间P99 500ms代码执行队列长度数据库连接池使用率容器内存占用使用PM2管理Node.js进程pm2 start ecosystem.config.js --env production日志收集方案应用日志Winston ELK访问日志Nginx日志 Filebeat错误追踪Sentry6. 踩坑与经验分享6.1 代码执行超时问题初期直接使用setTimeout限制执行时间发现无法彻底终止长时间运行的代码。最终解决方案在Docker run命令中添加--ulimit参数docker run --ulimit cpu1:1 -it code-runner结合进程树监控const { exec } require(child_process) function killProcessTree(pid) { exec(pkill -9 -P ${pid}, () { // 清理残留进程 }) }6.2 高并发下的资源竞争模拟笔试时发现当大量考生同时提交代码会导致Docker守护进程无响应MySQL连接池耗尽API响应时间飙升优化措施实现代码执行队列const Queue require(bull) const codeQueue new Queue(code execution, { limiter: { max: 10, // 每秒最大任务数 duration: 1000 } })动态扩展Docker容器池数据库连接池调优const pool mysql.createPool({ connectionLimit: 100, queueLimit: 500, acquireTimeout: 30000 })6.3 安全防护经验代码注入防护禁止执行危险系统命令过滤敏感文件访问使用只读文件系统数据安全考试期间加密传输代码定期备份数据库敏感操作审计日志防重放攻击请求签名验证一次性Token时间戳校验这套系统上线后我们的笔试效率提升了60%招聘成本降低了35%。最大的收获是掌握了全栈项目的架构设计能力特别是高并发场景下的系统优化经验。对于想要实现类似系统的开发者我的建议是从最小可行产品开始先实现核心的代码编辑和执行功能再逐步完善监控、安全等辅助模块。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价