Judge0 是一个生产级别的开源在线代码执行系统,采用分层架构设计,核心由 Ruby on Rails API 服务、Resque 后台任务队列、Isolate 沙箱执行环境以及 PostgreSQL 和 Redis 数据存储层组成。整个系统遵循前后端分离原则,通过 RESTful API 接收代码提交请求,由独立的工作进程负责在隔离环境中执行用户代码。
技术栈概览
Judge0 基于以下核心技术构建,每个组件都承担着特定职责,共同支撑起整个代码执行平台。
| 层级 | 技术选型 | 职责说明 |
|---|---|---|
| Web 框架 | Ruby on Rails 6.1 (API-only) | 接收 HTTP 请求,提供 REST API |
| 任务队列 | Resque + Resque-Scheduler | 管理后台代码执行任务 |
| 缓存/消息队列 | Redis 7.2.4 | 存储任务队列、缓存提交结果 |
| 数据库 | PostgreSQL 16.2 | 持久化提交记录、语言配置 |
| 沙箱隔离 | Isolate | Linux 命名空间隔离执行环境 |
| 序列化 | ActiveModel Serializers | JSON 格式数据转换 |
| 容器编排 | Docker Compose | 服务部署与扩缩容 |
Sources: Gemfile, docker-compose.yml
多服务架构
Judge0 采用 Docker Compose 部署模式,将系统拆分为四个独立服务,实现关注点分离与水平扩展能力。
graph TB
subgraph Docker_Network
subgraph API_Service["API 服务 (Rails)"]
RC[SubmissionsController]
SS[SubmissionSerializer]
BS[Base64Service]
end
subgraph Worker_Service["Worker 服务 (Resque)"]
IJ[IsolateJob]
IR[IsolateRunner]
IS[Isolate Sandbox]
end
PG[(PostgreSQL<br/>数据库)]
RD[(Redis<br/>任务队列)]
end
User([用户请求]) --> API_Service
API_Service --> PG
API_Service --> RD
RD --> Worker_Service
Worker_Service --> IS
Worker_Service --> PG
服务职责划分:
- Server 服务:运行 Rails 应用,处理 HTTP 请求,将代码提交任务入队到 Redis
- Worker 服务:运行 Resque 工作进程,从 Redis 消费任务,调用 Isolate 执行代码
- PostgreSQL:存储提交记录、语言配置、用户认证信息
- Redis:作为 Resque 后端存储任务队列
Sources: docker-compose.yml, scripts/server, scripts/workers
请求处理流程
当用户提交代码时,系统遵循严格的异步处理流程,确保高并发场景下的稳定性。
sequenceDiagram
participant U as 用户
participant API as Rails API
participant Redis as Redis Queue
participant Worker as Worker 进程
participant PG as PostgreSQL
participant IS as Isolate 沙箱
U->>API: POST /submissions
API->>PG: 创建 Submission 记录
API->>Redis: 入队 IsolateJob
API-->>U: 返回 token (202 Created)
Worker->>Redis: 获取 IsolateJob
Worker->>Worker: 初始化工作目录
Worker->>IS: 编译代码
IS-->>Worker: 编译结果
alt 编译成功
Worker->>IS: 执行代码
IS-->>Worker: 执行结果 (stdout/stderr/time/memory)
Worker->>PG: 更新 Submission 状态
else 编译失败
Worker->>PG: 更新为 CE 状态
end
U->>API: GET /submissions/{token}
API->>PG: 查询 Submission
API-->>U: 返回执行结果
同步等待模式:wait=true 参数可启用同步等待,控制器会轮询任务状态直至完成,适合需要即时获取结果的场景。
Sources: submissions_controller.rb, isolate_runner.rb
核心模块交互
SubmissionsController 控制器
控制器负责请求验证、参数解析和任务分发,是系统的 API 网关。
# 关键职责:
# 1. 参数白名单过滤 (submission_params)
# 2. Base64 编解码处理
# 3. 同步/异步执行模式切换
# 4. 批量提交支持
主要动作包括 create(创建提交)、show(查询单个结果)、index(分页列表)、batch_create(批量创建)和 batch_show(批量查询)。
Sources: submissions_controller.rb
IsolateJob 后台任务
IsolateJob 继承自 ApplicationJob,使用 Resque 作为队列适配器,负责在沙箱中执行代码。
class IsolateJob < ApplicationJob
retry_on RuntimeError, wait: 0.1.seconds, attempts: 100
queue_as ENV["JUDGE0_VERSION"].to_sym
def perform(submission_id)
# 1. 初始化工作目录 (isolate --init)
# 2. 编译代码 (如果需要)
# 3. 执行代码 (可多次运行取平均值)
# 4. 验证输出结果
# 5. 清理沙箱
end
end
任务队列名称基于 JUDGE0_VERSION 环境变量,支持多版本隔离。自动重试机制确保临时故障不会丢失任务。
Sources: isolate_job.rb
IsolateRunner 任务调度器
IsolateRunner 提供同步/异步两种任务调度方式。
module IsolateRunner
def self.perform_now(submission)
# 1. 立即入队任务
# 2. 轮询查询状态 (指数退避策略)
# 3. 阻塞等待结果 (最长 600 秒)
end
def self.perform_later(submission)
# 仅入队任务,立即返回 token
end
end
轮询策略采用指数退避:2s → 1s → 0.5s → 1s → 1.5s → ...
Sources: isolate_runner.rb
数据模型层
Submission 模型:核心实体,包含代码、输入输出、执行结果等完整信息。
class Submission < ApplicationRecord
# 自动 Base64 编解码存储
def source_code
@decoded_source_code ||= Base64Service.decode(self[:source_code])
end
# 关联语言配置
def language
@language ||= Language.unscoped.find_by(id: language_id)
end
# 枚举状态
enumeration :status
end
Language 模型:语言配置,定义编译命令和运行命令。
class Language < ApplicationRecord
validates :name, presence: true
validates :source_file, :run_cmd, presence: true, unless: -> { is_project }
default_scope { where(is_archived: false).order(name: :asc) }
end
Sources: submission.rb, language.rb
状态枚举系统
使用自定义枚举类管理提交状态,提供清晰的状态流转语义。
class Status < Enumerations::Base
values queue: { id: 1, name: 'In Queue' },
process: { id: 2, name: 'Processing' },
ac: { id: 3, name: 'Accepted' },
wa: { id: 4, name: 'Wrong Answer' },
tle: { id: 5, name: 'Time Limit Exceeded' },
ce: { id: 6, name: 'Compilation Error' },
sigsegv: { id: 7, name: 'Runtime Error (SIGSEGV)' },
sigxfsz: { id: 8, name: 'Runtime Error (SIGXFSZ)' },
sigfpe: { id: 9, name: 'Runtime Error (SIGFPE)' },
sigabrt: { id: 10, name: 'Runtime Error (SIGABRT)' },
nzec: { id: 11, name: 'Runtime Error (NZEC)' },
other: { id: 12, name: 'Runtime Error (Other)' },
boxerr: { id: 13, name: 'Internal Error' },
exeerr: { id: 14, name: 'Exec Format Error' }
end
Sources: status.rb
配置管理体系
Judge0 采用环境变量驱动配置,通过 judge0.conf 文件集中管理所有可配置项。
graph LR
CF[judge0.conf] -->|source| LC[load-config]
LC -->|export| ENV[环境变量]
ENV -->|读取| Rails[Rails 应用]
ENV -->|读取| Worker[Worker 进程]
subgraph 配置分类
SC[服务器配置]
WC[Worker 配置]
AC[认证配置]
RC[资源限制]
end
核心配置项分类:
| 分类 | 关键配置 | 默认值 |
|---|---|---|
| 服务器 | MAINTENANCE_MODE, ENABLE_WAIT_RESULT |
false, true |
| Worker | COUNT, MAX_QUEUE_SIZE, INTERVAL |
2*nproc, 100, 0.1s |
| 资源限制 | CPU_TIME_LIMIT, MEMORY_LIMIT |
5s, 128000KB |
| 认证 | AUTHN_HEADER, AUTHZ_HEADER |
X-Auth-Token, X-Auth-User |
Sources: config.rb, judge0.conf
数据库架构
erDiagram
SUBMISSION {
int id PK
text source_code
int language_id FK
text stdin
text expected_output
text stdout
int status_id
decimal time
int memory
string token UK
datetime created_at
datetime finished_at
}
LANGUAGE {
int id PK
string name
string compile_cmd
string run_cmd
string source_file
boolean is_archived
}
SUBMISSION ||--o{ LANGUAGE : "references"
数据库采用 PostgreSQL,支持复合索引优化查询性能,token 字段建立唯一索引加速结果查询。
Sources: schema.rb
安全隔离机制
代码执行安全由 Isolate 沙箱保证,通过 Linux cgroups 和命名空间实现进程级隔离。
# Isolate 执行命令示例
isolate \
-s \ # 安全模式
-b ${box_id} \ # 沙箱 ID
-M ${metadata_file} \ # 元数据输出
-t ${cpu_time} \ # CPU 时间限制
-m ${memory} \ # 内存限制
-k ${stack_size} \ # 栈大小限制
-p${max_procs} \ # 最大进程数
--cg \ # 启用 cgroups
--run -- /bin/bash run.sh
隔离策略:
- 资源限制:CPU 时间、内存、栈大小、文件大小、进程数
- 网络控制:
--share-net选项控制网络访问 - 环境变量:仅允许预定义环境变量传递
- 文件系统:
/etc目录设置noexec防止执行
Sources: isolate_job.rb, judge0.conf
序列化与缓存
ActiveModel Serializers 负责 JSON 序列化,支持按字段过滤和 Base64 编码。
class SubmissionSerializer < ActiveModel::Serializer
def self.default_fields
[:token, :time, :memory, :stdout, :stderr, :compile_output, :message, :status]
end
def source_code
base64_encoded ? object[:source_code] : object.source_code
end
end
Rails 缓存:查询结果使用 Rails.cache.fetch 缓存,默认 1 秒过期,通过 race_condition_ttl 防止缓存击穿。
Sources: submission_serializer.rb, submissions_controller.rb
部署架构
开发与生产环境采用不同的 Docker Compose 配置。
graph TB
subgraph Development
J0D[judge0:latest-dev] <--> DBD[PostgreSQL]
J0D <--> RDD[Redis]
end
subgraph Production
API[Server 容器] <--> DBP[PostgreSQL]
API <--> RDP[Redis]
W1[Worker 1] <--> RDP
W2[Worker 2] <--> RDP
Wn[Worker n] <--> RDP
end
Worker 数量默认设置为 2 * CPU核心数,可通过 COUNT 环境变量调整。
Sources: docker-compose.dev.yml, scripts/workers
相关文档
- 深入理解控制器实现:SubmissionsController 控制器
- 掌握后台任务处理:IsolateJob 沙箱执行任务
- 了解任务调度机制:IsolateRunner 任务调度器
- 配置文件参考:配置文件详解
- 容器化部署:Docker Compose 服务架构
相关条目
- 1-概述:开源在线代码执行系统
- 6-SubmissionsController 控制器
- 7-IsolateJob 沙箱执行任务