(来自鱼皮项目编程导航 - 程序员一站式编程学习交流社区,做您编程学习路上的导航员,作者:liyupi (程序员鱼皮) · GitHub)
有不正确的地方,欢迎更正
一、项目简介
YaoOJ(Yao Online Judge)在线判题系统是面向编程学习者与竞赛选手的自动化评测平台,核心功能包括用户在线答题、代码提交及系统自动判题。用户可选择题目,使用 Java等编程语言编写代码并提交(本项目只实现了Java),系统通过预设的输入输出用例、时间与内存限制,结合安全沙箱技术隔离代码运行环境,实现对代码正确性、效率及安全性的自动化验证。判题结果涵盖 “通过”“答案错误”“编译错误” 等十余种状态,支持实时反馈与历史记录查询。
项目技术栈采用Spring Cloud 微服务架构,前端基于 Vue3 与 Arco Design 组件库开发,实现用户界面与交互逻辑;后端通过消息队列(RabbitMQ)解耦判题任务,利用 Docker 构建代码沙箱确保环境隔离,并集成 MySQL 与 Redis 实现数据存储与缓存。系统设计注重可扩展性,支持多语言评测、分布式任务调度及权限管理(区分普通用户与管理员),同时提供开放接口供第三方调用。
该项目兼具学习价值与工程实践意义:一方面可深入理解编程思想、计算机基础与架构设计,突破传统 CRUD 开发局限;另一方面,系统复杂度高、技术栈前沿,适合作为简历亮点。项目参考了 Judge0(https://github.com/judge0/judge0)、HustOJ(https://github.com/zhblue/hustoj) 等开源方案,结合自主开发实现核心判题逻辑,旨在为用户提供稳定、高效的编程评测解决方案。
技术栈:SpringBoot、SSM、Docker、RabbitMQ、Redis、Knife4j、Spring Cloud Alibaba(如Nacos、Open Feign、Gateway)
核心业务流程:
项目功能:
-
题目模块
-
创建题目(管理员)
-
删除题目(管理员)
-
修改题目(管理员)
-
搜索题目(用户)
-
在线做题
-
提交题目代码
-
-
用户模块
-
注册
-
登录
-
-
判题模块
-
提交判题(结果是否正确与错误)
-
错误处理(内存溢出、安全性、超时)
-
自主实现 代码沙箱(安全沙箱)
-
开放接口(提供一个独立的新服务)
-
二、核心技术与实现
1. 数据库设计
- 核心表结构:
- 用户表(user):存储用户信息,包含角色字段(user/admin)用于权限控制。
- 题目表(question):存储题目内容、输入输出用例(JSON 格式)、判题配置(时间 / 内存限制)。
- 提交表(question_submit):记录提交记录,包括代码内容、判题状态、执行结果(JSON 格式)。
- 索引优化:在
userId
、questionId
字段创建索引,提升查询效率;使用ASSIGN_ID
策略生成分布式 ID,避免 ID 泄露。
2. 判题流程与策略
- 核心流程:
- 用户提交代码→题目服务校验权限并存储提交记录。
- 判题服务从队列获取任务→调用代码沙箱执行代码。
- 沙箱返回执行结果(如运行时间、内存占用、输出结果)。
- 判题服务对比用户输出与标准答案,更新判题状态(Accepted/Wrong Answer 等)。
- 策略模式:针对不同编程语言(如 Java、C++)实现差异化判题逻辑,例如 Java 需编译后执行,C++ 直接运行可执行文件。
3. 代码沙箱设计
- 核心目标:隔离用户代码,防止恶意操作(如内存溢出、文件读写),限制资源占用(CPU / 内存 / 时间)。
- 实现方案:
- Java 原生实现:通过
Process
类执行编译命令,结合SecurityManager
限制文件读写、网络连接等权限,但存在权限控制粒度粗的问题。 - Docker 容器化:利用 Docker 的
cgroups
和namespaces
实现资源隔离,通过镜像管理运行环境(如 OpenJDK 镜像),支持多语言(Java/C++/Go)。核心流程:拉取镜像→创建容器→挂载代码目录→执行判题命令→回收资源。 - 安全性优化:设置容器内存上限(
-m 100m
)、只读根文件系统(--read-only
)、禁用网络(--network none
),结合 Seccomp 过滤危险系统调用。
- Java 原生实现:通过
4. 微服务架构改造
- 架构拆分:
- 用户服务:处理注册、登录、权限校验。
- 题目服务:管理题目增删改查、判题任务提交。
- 判题服务:独立服务,通过消息队列(RabbitMQ)接收判题任务,调用代码沙箱执行判题逻辑。
- 网关服务:统一路由(Spring Cloud Gateway)、鉴权、限流(Sentinel)。
- 服务通信:
- OpenFeign:实现服务间远程调用,如题目服务调用用户服务获取用户信息。
- 消息队列:解耦判题任务,题目服务提交任务到队列,判题服务异步消费,避免同步调用超时问题。
三、关键挑战与解决方案
1. 安全性问题
- 风险点:用户代码可能包含恶意指令(如删除文件、发起网络请求)。
- 解决方案:
- 代码沙箱层:Docker 容器隔离 + Seccomp 过滤系统调用 + Java 安全管理器限制文件操作。
- 网关层:通过
GlobalAuthFilter
拦截包含敏感路径(如/inner
)的请求,防止未授权访问。
2. 性能优化
- 异步化改造:将判题任务放入消息队列,避免同步处理阻塞接口响应。
- 资源复用:Docker 容器复用(非一次性容器),减少镜像拉取和容器创建开销。
- 缓存机制:使用 Redis 缓存用户登录状态,避免频繁查询数据库。
3. 多语言支持
- 实现方式:为每种语言定制镜像(如
openjdk:8-alpine
、gcc:latest
),代码沙箱根据语言参数选择对应镜像执行。 - 编译命令抽象:通过工厂模式动态生成编译命令(如
javac
、g++
),解耦语言差异。
四、学习总结
技术收获
- 深入理解容器化技术(Docker)在隔离与资源管理中的应用。
- 掌握微服务架构设计(服务拆分、通信机制、网关路由)。
- 实践设计模式(工厂模式、策略模式、代理模式)在代码架构中的应用。
- 提升数据库设计能力(字段类型选择、索引优化、JSON 字段处理)。
前端仓库:
后端仓库
代码沙箱仓库
后端微服务改造仓库:
五、项目页面展示
前端登录页面:
前端登录输入信息页面:
前端角色user页面:
前端角色admin页面:
前端“浏览题目”页面:
前端“浏览题目提交”页面:
前端“创建题目”页面:
前端“创建题目”输入页面:
前端“主页”页面
前端“做题”页面
聚合文档user-service页面(部分):
聚合文档question-service页面(部分):
聚合文档judge-service页面(部分):
后端yaooj-backend项目目录:
后端microservice改造目录:
后端yaooj-code-sandbox运行页面
虚拟机运行照片:
虚拟机连接SSH照片:
虚拟机项目所在地照片: