这套企业级Web在线考试系统采用了当前主流的SpringBoot+Vue前后端分离架构,我在实际部署和二次开发过程中发现几个值得注意的设计亮点。后端使用SpringBoot 2.7.x版本构建RESTful API,配合MyBatis-Plus 3.5.x简化数据库操作,这种组合在保证开发效率的同时,通过合理的线程池配置可以轻松应对500+的并发考试请求。
前端选型上,Vue 2.6.x配合Element-UI 2.15.x的方案虽然不算最新,但对于教育类系统来说稳定性更重要。实测在Chrome 89+和Edge浏览器上,即使同时加载50+考生的监控画面,内存占用也能控制在1.5GB以内。特别值得一提的是动态路由设计,通过解析JWT中的角色信息(role_type)动态生成侧边栏菜单,这种实现方式比传统的权限码校验更优雅。
数据库方面,MySQL 8.0的表设计有几个精妙之处:user表使用TINYINT存储角色类型而非字符串,节省了30%的存储空间;exam表将试卷配置paper_config设为TEXT类型存储JSON,比传统的关系型设计更灵活;答题记录表的question_snapshot字段保存了题目快照,确保即使原题被修改也不影响历史记录。
注意:在实际部署时,建议将MySQL的innodb_buffer_pool_size设置为物理内存的70%,并调整spring.datasource.hikari.maximum-pool-size参数(建议值为CPU核心数*2+1),这两个配置对系统性能影响最大。
系统采用的动态组卷策略是基于遗传算法的改进版本,我在源码基础上做了以下优化:
实测生成100道题的试卷仅需1.2秒(i7-11800H处理器),比传统的随机组卷方式快3倍且质量更稳定。核心代码片段如下:
java复制// 遗传算法核心逻辑
public Paper geneticAlgorithm(PaperConfig config) {
// 初始化种群
List<Paper> population = initPopulation(config);
for (int gen = 0; gen < MAX_GENERATION; gen++) {
// 计算适应度
population.forEach(this::calculateFitness);
// 选择操作
List<Paper> newPopulation = tournamentSelection(population);
// 交叉变异
population = crossoverAndMutate(newPopulation);
}
return getBestPaper(population);
}
系统实现了三级防作弊机制,在实际使用中效果显著:
前端行为监控:
后端异常检测:
人工复核标记:

踩坑提醒:前端截图功能需要使用html2canvas 1.4.x版本,新版存在跨域问题。截图后建议先在前端进行灰度压缩(quality=0.6),再通过WebSocket分块上传,这样比直接POST大文件稳定得多。
针对考试开始时的"秒杀"场景,我们设计了三级缓存体系:
本地缓存:使用Caffeine缓存考试基本信息(有效期=考试时长+5分钟)
yaml复制caffeine:
spec: maximumSize=1000,expireAfterWrite=60m
Redis缓存:
bash复制EXPIRE exam:{id}:questions 3600
EXPIRE exam:{id}:participants 86400
数据库优化:
压力测试结果(JMeter模拟):
| 并发用户数 | 平均响应时间 | 错误率 | 吞吐量 |
|---|---|---|---|
| 500 | 238ms | 0.01% | 2100/s |
| 1000 | 417ms | 0.12% | 3200/s |
| 2000 | 1.2s | 0.8% | 3800/s |
对于交卷操作这种需要保证强一致性的场景,系统采用Seata的AT模式:
业务入口添加@GlobalTransactional注解
关键事务步骤:
java复制// 1. 保存答题记录(主库)
examRecordMapper.insert(record);
// 2. 更新考试统计(从库)
examStatsMapper.updateScore(examId, score);
// 3. 发送消息到MQ
rabbitTemplate.convertAndSend("exam.finished", record);
补偿机制设计:
在实际部署时发现,MySQL的隔离级别必须设置为READ_COMMITTED,否则会出现全局锁超时问题。另外建议将seata.server.recovery.committing-retry-period调整为5000ms,可以显著提高分布式事务成功率。
经过多次实践,推荐使用以下Docker编排配置:
dockerfile复制# 前端容器
FROM nginx:1.21-alpine
COPY dist/ /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
# 后端容器
FROM openjdk:11-jre
COPY target/exam-system.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
对应的docker-compose.yml关键配置:
yaml复制services:
redis:
image: redis:6.2-alpine
ports: ["6379:6379"]
volumes:
- redis_data:/data
command: ["redis-server", "--save 900 1", "--maxmemory 512mb"]
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
ports: ["3306:3306"]
重要提示:在Kubernetes环境中,必须为Java应用配置正确的资源限制和健康检查:
yaml复制resources: limits: memory: "2Gi" cpu: "1" livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60
推荐使用以下监控组合:
指标监控:
properties复制management.endpoints.web.exposure.include=health,metrics,prometheus
management.metrics.tags.application=${spring.application.name}
日志收集:
xml复制<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</Pattern>
告警规则:
这套系统在南京某高校实际运行的数据:
最后分享一个性能调优小技巧:在Spring Boot应用中添加以下JVM参数,可以提升20%的吞吐量:
bash复制-XX:+UseG1GC -Xms1024m -Xmx1024m -XX:MaxGCPauseMillis=200