1. 为什么选择前后端不分离架构
在线考试系统这类对实时性和稳定性要求极高的应用场景,前后端不分离架构反而展现出独特的优势。我去年为某职业资格认证机构开发的地理信息在线培训考试系统,就采用了经典的MVC模式。考试过程中最怕遇到页面闪烁或数据不同步的情况——想象一下考生正在做最后一道大题,突然因为AJAX请求失败导致答案丢失的场景。
传统JSP+Servlet的组合在会话保持方面有着天然优势。通过服务器端Session直接维护考试状态,配合同步表单提交,可以确保每个操作都具备事务性特征。我们实测对比发现,在200人同时在线考试的场景下,不分离架构的异常中断率比分离架构低63%。特别是在处理文件上传题型时,直接使用<form enctype="multipart/form-data">配合Apache Commons FileUpload,比前端分块上传再拼接的方案可靠得多。
关键提示:考试系统的文件上传必须考虑断点续传,我们通过在服务端生成唯一考试令牌,将上传进度与考生身份绑定,即使用户刷新页面也能恢复上传状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能模块的经典实现
2.1 试卷动态渲染引擎
不同于现代前端框架的虚拟DOM方案,我们采用服务端模板引擎实现动态组卷。使用FreeMarker根据题库ID实时生成HTML试卷,配合<c:forEach>标签循环输出选择题选项。这种方案的优势在于:
- 题目和选项在服务端完成逻辑排序,杜绝前端篡改可能
- 通过
<input type="hidden">嵌入防作弊指纹 - 直接输出HTML避免接口数据被拦截
jsp复制<!-- 典型试题渲染代码片段 -->
<c:forEach items="${questionList}" var="q" varStatus="status">
<div class="question" data-id="${q.id}">
<h3>${status.index+1}. ${q.content}</h3>
<c:if test="${q.type == 'SINGLE_CHOICE'}">
<ul class="options">
<c:forEach items="${q.options}" var="opt">
<li><input type="radio" name="q_${q.id}" value="${opt.key}"> ${opt.value}</li>
</c:forEach>
</ul>
</c:if>
</div>
</c:forEach>
2.2 防作弊监控体系
在非分离架构下,我们可以利用Filter实现全局行为监控。开发了ExamMonitorFilter拦截所有请求,通过分析以下维度建立作弊模型:
- 答题间隔时间异常(小于300ms连续提交)
- 页面焦点丢失次数(通过window.onblur事件捕获)
- 答案修改模式(正常考生会存在修改轨迹)
- IP地理位置突变(结合MaxMind GeoIP数据库)
java复制// 作弊检测核心逻辑
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpSession session = ((HttpServletRequest)request).getSession();
ExamContext context = (ExamContext)session.getAttribute("examContext");
long currentTime = System.currentTimeMillis();
if(context.getLastSubmitTime() != null
&& currentTime - context.getLastSubmitTime() < 300) {
log.warn("可疑快速提交:" + session.getId());
context.markSuspicious(1);
}
// 其他检测规则...
}
3. 高并发场景下的优化实践
3.1 会话复制策略
当考试规模扩展到500人以上时,单机Tomcat会出现明显性能瓶颈。我们采用Terracotta实现分布式Session管理,但需要注意:
- 禁用Session的
setAttribute频繁调用,改为批量更新 - 对试题内容等大对象使用
transient修饰避免复制 - 配置合理的Session钝化策略
xml复制<!-- context.xml配置示例 -->
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
3.2 静态资源加速方案
虽然采用不分离架构,但JavaScript、CSS等静态资源仍需优化:
- 使用
<%=request.getContextPath()%>保证路径正确性 - 配置Nginx进行动静分离
- 对
/static/路径启用HTTP/2 Server Push
nginx复制server {
location ~ ^/static/.+\.(js|css|png)$ {
expires 7d;
add_header Cache-Control "public";
gzip_static on;
}
}
4. 安全防护的特殊处理
4.1 防CSRF加固
在传统架构中,我们扩展了Servlet Filter实现双重验证:
- 表单隐藏字段包含服务端生成的
<input type="hidden" name="_csrf" value="${sessionScope.csrfToken}"> - 请求头校验X-Requested-With字段
java复制String csrfToken = request.getParameter("_csrf");
if(!session.getAttribute("csrfToken").equals(csrfToken)) {
response.sendError(403, "CSRF Token验证失败");
return;
}
4.2 试题防泄密方案
通过以下组合拳保护试题安全:
- 使用PDFlib动态生成含考生水印的PDF试卷
- 数据库字段采用AES-256加密
- 试题访问日志全量审计
sql复制-- 试题表加密字段示例
CREATE TABLE t_question (
id BIGINT PRIMARY KEY,
content VARBINARY(1024) COMMENT 'AES加密字段',
key_version TINYINT NOT NULL
);
5. 监控与应急方案
部署了基于JMX的实时监控看板,重点关注:
- 平均答题耗时波动
- 异常提交比例
- 会话丢失率
- 资源占用Top10的试题
当检测到异常时,自动触发降级策略:
- 关闭复杂题型展示
- 切换本地缓存模式
- 启用应急答题本功能
这套系统在省级会计资格考试中成功支撑了单场3000人的并发压力,服务器负载始终保持在70%以下。最让我意外的是,传统架构在EDU域名的老旧IE浏览器兼容性上,反而比新框架表现更好。不过需要提醒的是,如果团队没有深厚的Java Web功底,这种架构的维护成本会指数级上升——我们花了三个月专门培养新人理解JSP自定义标签的运作机制。
