news 2026/7/1 18:53:22

毕业设计实战:从零构建一个高可用的刷题平台后端架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
毕业设计实战:从零构建一个高可用的刷题平台后端架构


毕业设计实战:从零构建一个高可用的刷题平台后端架构

摘要:许多学生在毕业毕业设计实战:从零构建一个高可用的刷题平台后端架构

摘要:许多学生在毕业设计中选择开发刷题平台,却常因缺乏工程经验而陷入性能瓶颈、接口混乱或数据一致性问题。本文基于真实毕业设计场景,详解如何使用 Spring Boot + MyBatis Plus + Redis 构建具备题目管理、用户提交、判题回调等核心功能的后端系统。通过引入消息队列解耦判题服务、利用 Redis 缓存热点题目、设计幂等性提交接口,显著提升系统吞吐量与稳定性。读者将获得一套可直接复用的模块化代码结构与部署 checklist。


1. 背景痛点:学生项目常见“三座大山”

毕业设计里做“刷题平台”听起来简单,落地时却常被以下问题卡住:

  1. 判题阻塞:同步判题导致线程长时间挂起,并发一上来整站 504。

  2. 重复提交:前端连点两下“提交”,数据库里出现两条记录,用户一脸懵。

  3. 冷启动延迟:题目列表接口每次全表扫描,首页打开 3 s 起步,答辩现场直接翻车。

这些痛点本质上是“学生项目”与“工程系统”之间的鸿沟:功能代码能跑,但缺容错、缺横向扩展、缺观测手段。下文用一套最小可用、却可线性扩展的架构,带你把“玩具”升级成“产品”。


2. 技术选型:为什么不是 Django,也不是本地内存

维度Spring BootDjango/Flask结论
依赖注入与 AOP原生支持靠第三方Spring 生态对事务、幂等、重试的封装更成熟
横向扩展无状态 Jar + 任意注册中心Python GIL 限制多进程利用率Java 多线程模型更适合 CPU 密集判题
社区组件MyBatis Plus、Spring Cloud、RocketMQ相对分散企业级方案直接搬来即用

缓存方案对比:

  • 本地内存:进程重启即失效,多实例时缓存漂移,无法横向扩展。
  • Redis:独立进程,可集群;支持 TTL、LRU、Pub/Sub,天然适合“热点题目”与“判题结果”缓存。

综上,后端主栈锁定Spring Boot 2.7 + MyBatis Plus + Redis 6.x + RocketMQ 4.9,部署在 2C4G 单机上即可抗住毕业设计答辩并发。


3. 模块划分与核心实现

系统分三层:网关层(Nginx)、业务层(Spring Boot)、判题层(Sandbox)。本文聚焦业务层,内部再拆为:

  • 题目服务(Problem Service)
  • 提交服务(Submit Service)
  • 判题回调(Judge Callback)

3.1 题目服务:缓存 + 分页 + 索引

热点题目(近 7 日提交量 Top 200)在 Redis 采用hash结构缓存,字段即题号,值序列化为 JSON;冷数据走 DB,分页用 MyBatis Plus 的Page对象。缓存穿透用布隆过滤器拦截,缓存雪崩加随机 60–120 s 的 TTL jitter。

3.2 提交服务:接口幂等性设计

前端提交时携带client_submit_id(UUID),后端用数据库唯一索引实现幂等:

UNIQUE KEY uk_user_submit (user_id, client_submit_id)

重复请求直接返回原结果,避免重复入库。核心代码见第 4 节。

3.3 判题回调:消息队列解耦

提交服务只负责“写记录 + 发消息”,不等待判题结果;Sandbox 判完后向 MQ 发送JudgeFinishedEvent,业务层消费后更新状态。事件体例如下:

{ "submitId": 142857, "result": "AC", "time": 120, "memory": 65536 }

消费端幂等:利用submitId做幂等键,更新前判断状态是否已终态(AC/WA/TLE 等),防止重复累加通过数。


4. 关键代码片段(含注释)

4.1 SubmitController——接收提交、幂等保护

@RestController @RequestMapping("/api/submit") @RequiredArgsConstructor public class SubmitController { private final SubmitService submitService; /** * 1. 幂等键:clientSubmitId * 2. 事务边界:仅落库与发消息,不等待判题 */ @PostMapping public ApiResult<SubmitDTO> submit(@LoginUser Long userId, @Valid @RequestBody SubmitRequest req) { // 重复提交直接返回 SubmitDTO exist = submitService.getByUserAndClientId(userId, req.getClientSubmitId()); if (exist != null) { return ApiResult.success(exist); } // 新提交:本地事务 = 写库 + 发 MQ SubmitDTO dto = submitService.doSubmit(userId, req); return ApiResult.success(dto); } }

4.2 JudgeEventConsumer——消费判题结果,保证幂等

@Component @RocketMQConsumer(topics = "topic_judge_result") @Slf4j @RequiredArgsConstructor public class JudgeEventConsumer { private final SubmitService submitService; private final RedisTemplate<String, String> redisTemplate; @Override public void onMessage(JudgeFinishedEvent event) { Long submitId = event.getSubmitId(); String key = "judge:result:" + submitId; // 1. 利用 Redis setnx 做分布式锁,防并发重复消费 Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, "1", Duration.ofMinutes(5)); if (Boolean.FALSE.equals(absent)) { log.warn("duplicate consume, submitId={}", submitId); return; } // 2. 更新库,版本号乐观锁兜底 boolean updated = submitService.updateResult(event); if (!updated) { log.error("update submit result failed, event={}", event); } } }

5. 性能与安全:并发、防刷、SQL 注入

  1. 竞争条件
    更新提交状态时使用乐观锁version字段,CAS 失败重试 3 次,仍失败则日志告警人工介入。

  2. 防刷机制

    • 接口限流:基于 Redis 的令牌桶,每用户 10 次/60 s。
    • 验证码:同一 IP 5 min 内提交超过 20 次弹出图形验证码。
    • 代码相似度检测:引入sim命令,重复率 > 90 % 直接判 0 分并记录。
  3. SQL 注入
    MyBatis Plus 内置#{}预编译,杜绝拼接;动态排序用WrapperorderBy方法,内部白名单校验列名。


6. 生产级避坑 checklist

坑点现象解决
索引缺失题目列表按difficulty + create_time查询 2 s联合索引(difficulty, create_time)后降至 20 ms
判题超时无重试Sandbox 宕机,消息消费成功但结果丢失消费端 ack 前检查返回码,非 200 抛异常,MQ 自动重试 16 次
日志缺失线上出错无法复现接入traceId透传,Controller、MQ、线程池统一 MDC 打印
Redis 大 Key缓存整表select *导致 value 5 MB,网卡打满拆分为hash分片,只缓存必要字段
大事务提交接口里同步调用判题 + 写库 + 更新通过数,锁等待 3 s拆分为“写提交记录”与“更新通过数”两个事务,后者异步

7. 部署与可观测

  1. CI 脚本mvn -T 1C clean package -Dmaven.test.skip=true打出 fat-jar,配合systemd托管。
  2. Dockerfile仅 30 行,基于openjdk:17-jre-slim, layers 缓存缓存依赖。
  3. Prometheus + Grafana
    • JVM 级:GC、线程数、内存;
    • 应用级:QPS、RT、提交成功率;
    • 业务级:7 日 AC 率、题目冷热分布。
  4. 告警:RT > 1 s 持续 2 min 或错误率 > 5 % 即刻飞书群机器人推送。

8. 后续思考:如何支持多语言判题沙箱?

当前 Sandbox 只支持 C/C++,如果后续想扩展 Java、Python、Go,需要:

  1. 镜像隔离:用runsckata-containers替代裸docker run,防止ptrace逃逸。
  2. 资源配额:CPU、内存、seccomp 统一配置,不同语言复用同一套 cgroup 模板。
  3. MQ 路由:根据语言类型投递到不同 topic,消费端水平扩容互不干扰。
  4. 结果归一化:统一返回cpu_timememoryexit_code,业务层零改动。

整套代码已开源在 GitHub,欢迎 fork 并提交 Pull Request,一起把毕业设计项目做成能写进简历的工业级作品。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 7:14:07

智能客服Agent建设:从架构设计到生产环境最佳实践

背景痛点&#xff1a;电商大促夜的“翻车”现场 去年双十一&#xff0c;我们组负责的智能客服在零点流量洪峰中“崩”得很有节奏&#xff1a; 用户问“我买的 iPhone 能 12 期免息吗&#xff1f;”——Bot 回复“请提供订单号”。用户追问“订单号在哪看&#xff1f;”——Bo…

作者头像 李华
网站建设 2026/7/1 18:50:48

Docker跨架构配置稀缺资源包(含buildkit优化参数模板、multi-arch manifest校验工具、内核ABI对照速查表)——仅限前500名开发者领取

第一章&#xff1a;Docker跨架构配置的核心挑战与演进脉络在云原生基础设施日益异构化的今天&#xff0c;Docker镜像不再仅限于x86_64平台。ARM64服务器、Apple Silicon Mac开发机、RISC-V边缘设备等多元硬件生态的崛起&#xff0c;迫使开发者直面构建、推送与运行跨架构容器镜…

作者头像 李华
网站建设 2026/7/1 16:00:29

【工业级Docker安全加固白皮书】:通过seccomp、AppArmor、rootless运行与cgroup v2实现等保三级合规

第一章&#xff1a;工业级Docker安全加固白皮书导论在现代云原生基础设施中&#xff0c;Docker容器已成为交付与运行关键业务应用的事实标准。然而&#xff0c;其轻量、共享内核的特性也放大了配置不当、镜像污染、权限滥用等风险。本白皮书聚焦于工业场景下对高可用性、强合规…

作者头像 李华
网站建设 2026/6/25 17:13:09

AI 辅助开发实战:高效完成 Unity2D 毕业设计的工程化路径

AI 辅助开发实战&#xff1a;高效完成 Unity2D 毕业设计的工程化路径 1. 学生开发者在 Unity2D 项目中常见的痛点 毕业设计往往周期短、人手少&#xff0c;却要求“看起来像个完整游戏”。我辅导过十几届学弟妹&#xff0c;大家踩的坑高度重合&#xff1a; 动画状态爆炸&…

作者头像 李华