简介:本资源是一套基于Django框架开发的在线编程竞赛平台完整源码,面向Python后端开发者、Web全栈学习者及高校计算机专业学生,用于理解并复现典型OJ(Online Judge)系统的核心架构与工程实践。项目覆盖用户管理、题库维护、竞赛组织、代码提交判题、API接口设计、缓存优化及UV统计等全流程功能,具备生产级模块划分与可扩展性设计。压缩包共193个文件,以163个Python源码文件为主体,支撑业务逻辑与Django应用结构;辅以7个配置文件(如supervisord.conf、nginx.conf、Dockerfile等),体现服务部署与容器化能力;另有HTML模板、Shell脚本、SSL证书及Markdown文档,构成开箱即用的开发-部署闭环。包体仅282KB,轻量但结构完整,目录清晰、模块解耦度高,适合深入学习Django中REST API、权限控制、异步任务与系统集成等进阶实践。目前已有31人学习下载。
1. 这不是另一个“Django博客模板”,而是一个可上线的在线编程竞赛平台原型
你解压(源码)基于Django框架的在线编程竞赛平台.zip后,看到的不是manage.py + polls/的教学示例,而是一套完整覆盖「题目管理→代码提交→实时判题→排行榜→用户权限分级」闭环的生产级骨架。它不依赖 Vue 或 React 前端框架,纯 Django 模板渲染 + jQuery AJAX 实现交互,对中小团队快速部署 OJ(Online Judge)类系统极具实操价值。尤其适合高校算法课实训平台、企业内部编程考核系统或开源技术社区的轻量级竞赛服务——不需要 Docker 编排、不强求 Kubernetes,一台 2 核 4G 的云服务器 + Nginx + PostgreSQL 就能跑通核心链路。注意:该 ZIP 包不含预编译二进制判题沙箱(如 seccomp 隔离的 C++ 编译器),判题逻辑走 Python subprocess 调用本地 gcc/g++/python3 解释器,因此安全边界依赖操作系统级用户隔离与超时控制,不可直接用于面向公网的高并发竞赛场景,但作为教学演示、内网测评或二次开发基线完全可靠。
2. 从 ZIP 解包到 Django 项目结构还原:识别关键模块与依赖约束
2.1 解压后必须验证的 4 类文件完整性
该 ZIP 包并非简单压缩,其目录结构隐含运行约束。解压后需立即检查以下四类文件是否存在且路径正确(以典型解压路径/opt/oj-platform/为例):
| 文件类型 | 必须存在路径 | 作用说明 |
|---|---|---|
| Django 核心配置 | /opt/oj-platform/oj_platform/settings/production.py | 生产环境专用配置,含 SECRET_KEY、DEBUG=False、ALLOWED_HOSTS 等硬性参数,不可用settings/base.py替代 |
| 判题引擎脚本 | /opt/oj-platform/judge/judge_worker.py | 独立于 Django 的长进程,通过 Redis 队列接收待判题任务,调用gcc -o /tmp/xxx /tmp/xxx.c && /tmp/xxx < input.txt > output.txt执行,超时由signal.alarm()控制 |
| Nginx 反向代理配置片段 | /opt/oj-platform/deploy/nginx.conf | 非完整 nginx.conf,而是include /opt/oj-platform/deploy/nginx.conf;引用的 location 块,专用于/api/judge//static//media/路由分发 |
| Supervisor 进程管理配置 | /opt/oj-platform/deploy/supervisord.conf | 定义oj-web(gunicorn)、oj-judge(judge_worker.py)、celery-worker(异步任务)三个进程组,含autostart=true和stopwaitsecs=30等关键重启策略 |
提示:若解压后缺失
deploy/目录,说明 ZIP 包被截断或下载不完整。使用unzip -t "(源码)基于Django框架的在线编程竞赛平台.zip"校验 CRC32 值,失败则重新下载。不要尝试用zip -F修复,该包无冗余校验段。
2.2 依赖安装必须锁定 Python 版本与关键包版本
项目requirements.txt中明确要求Django==4.2.11(非最新 5.x),因判题模块judge/utils.py使用了django.db.models.signals.post_save的旧版信号注册语法。执行以下命令前,确认系统 Python 版本为 3.9–3.11:
# 创建隔离环境(推荐 pyenv 或 system python -m venv) python3.10 -m venv /opt/oj-platform/venv source /opt/oj-platform/venv/bin/activate # 安装时强制忽略兼容性警告,但保留 psycopg2-binary(PostgreSQL 驱动) pip install --no-deps -r requirements.txt pip install "Django==4.2.11" "psycopg2-binary==2.9.7" "redis==4.6.0" "gunicorn==21.2.0"注意:
mysqlclient不在依赖列表中——该项目默认使用 PostgreSQL。若需切换 MySQL,必须修改settings/production.py中DATABASES['default']['ENGINE']为'django.db.backends.mysql',并额外安装mysqlclient==2.2.4,否则python manage.py migrate会报django.core.exceptions.ImproperlyConfigured: 'mysql' isn't an available database backend.
2.3 数据库迁移与初始超级用户创建
迁移前必须确保 PostgreSQL 服务已启动且oj_platform数据库已创建(非仅用户):
# 登录 psql 创建数据库(假设 PostgreSQL 用户为 postgres) sudo -u postgres psql -c "CREATE DATABASE oj_platform;" sudo -u postgres psql -c "CREATE USER oj_user WITH PASSWORD 'StrongPass123!';" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE oj_platform TO oj_user;" # 执行 Django 迁移(指定 settings 模块路径) cd /opt/oj-platform python manage.py migrate --settings=oj_platform.settings.production python manage.py createsuperuser --settings=oj_platform.settings.production迁移成功后,auth_user、problem_problem、submission_submission等 12 张表将生成。特别注意problem_problem表的test_case_zip字段为models.FileField,实际存储路径由settings.production.MEDIA_ROOT = '/opt/oj-platform/media/'决定,需确保该目录存在且 Web 服务用户(如www-data)有写权限。
3. Nginx + Gunicorn + Supervisor 三件套部署:让平台真正可访问
3.1 配置 Nginx 反向代理与静态资源服务
将deploy/nginx.conf内容合并至主配置(如/etc/nginx/sites-enabled/oj.conf),关键部分如下:
upstream oj_web { server 127.0.0.1:8000; } server { listen 80; server_name oj.example.com; # 静态资源直接由 Nginx 服务,不经过 Django location /static/ { alias /opt/oj-platform/staticfiles/; expires 1y; add_header Cache-Control "public, immutable"; } # 媒体文件(题目测试用例 ZIP、用户上传代码)也由 Nginx 服务 location /media/ { alias /opt/oj-platform/media/; expires 1h; } # API 请求转发给 Gunicorn location /api/ { proxy_pass http://oj_web; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 根路径交由 Django 处理 location / { proxy_pass http://oj_web; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }提示:
/static/路径必须与python manage.py collectstatic --settings=oj_platform.settings.production输出目录一致。若collectstatic报错OSError: [Errno 13] Permission denied: '/opt/oj-platform/staticfiles',执行sudo chown -R www-data:www-data /opt/oj-platform/staticfiles。
3.2 Gunicorn 启动参数详解与性能调优
supervisord.conf中oj-web进程组定义如下:
[program:oj-web] command=/opt/oj-platform/venv/bin/gunicorn --bind 127.0.0.1:8000 --workers 3 --worker-class sync --timeout 120 --max-requests 1000 --graceful-timeout 30 --log-level info oj_platform.wsgi:application directory=/opt/oj-platform user=www-data autostart=true autorestart=true redirect_stderr=true stdout_logfile=/var/log/oj/web.log--workers 3:按 2 核 CPU 计算,2 × 2 + 1 = 5理论值,但判题模块占用大量 CPU,故降为 3 避免争抢;--timeout 120:必须 ≥ 判题脚本JUDGE_TIMEOUT = 60(秒),否则 Gunicorn 在判题完成前就 kill worker;--max-requests 1000:防止内存泄漏,每处理 1000 个请求后重启 worker;--graceful-timeout 30:确保正在执行的判题请求有 30 秒优雅退出时间。
3.3 Supervisor 管理判题进程与故障自愈
supervisord.conf中oj-judge组是平台核心,其配置决定判题稳定性:
[program:oj-judge] command=/opt/oj-platform/venv/bin/python /opt/oj-platform/judge/judge_worker.py directory=/opt/oj-platform user=oj-judge # 必须创建独立用户,禁止用 root 或 www-data autostart=true autorestart=true startretries=3 stopwaitsecs=60 redirect_stderr=true stdout_logfile=/var/log/oj/judge.log environment=PYTHONPATH="/opt/oj-platform"注意:
user=oj-judge要求提前创建该系统用户,并赋予/opt/oj-platform/media/读写权限及/tmp/执行权限:sudo useradd -r -s /bin/false oj-judge sudo chown -R oj-judge:oj-judge /opt/oj-platform/media/ sudo setfacl -R -m u:oj-judge:rwx /tmp/
启动全部服务后,执行sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl start all,再用curl http://localhost/api/health/验证接口返回{"status":"ok","judge_status":"ready"}。
4. 判题模块深度配置:控制超时、内存限制与语言支持
4.1 修改judge/config.py中的 5 个关键安全参数
该文件定义判题沙箱行为,直接关系到服务器安全。必须根据硬件调整:
| 参数名 | 默认值 | 推荐值(2核4G) | 说明 |
|---|---|---|---|
JUDGE_TIMEOUT | 30 | 60 | 单次判题最大运行时间(秒),Python 题目常需 40+ 秒 |
JUDGE_MEMORY_LIMIT | 134217728 (128MB) | 268435456 (256MB) | 进程虚拟内存上限,C++ STL 容器易突破 128MB |
JUDGE_PROCESS_LIMIT | 50 | 100 | 子进程数限制,避免 fork 炸裂 |
JUDGE_MAX_FILE_SIZE | 1048576 (1MB) | 5242880 (5MB) | 用户提交代码最大体积,支持大算法题 |
SUPPORTED_LANGUAGES | ["c", "cpp", "python"] | ["c", "cpp", "python", "java"] | 添加 Java 需确保系统已安装 OpenJDK 17 |
修改后需重启oj-judge进程:sudo supervisorctl restart oj-judge。
4.2 Java 支持的三步落地(非默认启用)
添加 Java 支持需手动补全:
安装 JDK 并设环境变量
sudo apt install openjdk-17-jdk echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' | sudo tee -a /etc/environment source /etc/environment在
judge/language_handlers.py中注册 Java 处理器# 新增 JavaCompiler 类(位于 judge/language_handlers.py) class JavaCompiler(BaseCompiler): def compile(self, src_path, exe_path): # 编译命令:javac -d /tmp/xxx/ /tmp/xxx/Main.java cmd = ["javac", "-d", os.path.dirname(exe_path), src_path] return self._run_command(cmd, timeout=30) def run(self, exe_path, stdin_path, stdout_path, stderr_path): # 运行命令:java -cp /tmp/xxx/ Main < input.txt > output.txt class_dir = os.path.dirname(exe_path) main_class = "Main" cmd = ["java", "-cp", class_dir, main_class] return self._run_command(cmd, stdin_path, stdout_path, stderr_path, timeout=self.timeout)更新
SUPPORTED_LANGUAGES并重启判题进程
在judge/config.py中将"java"加入列表,执行sudo supervisorctl restart oj-judge。
4.3 测试判题链路:用 curl 模拟一次提交
验证判题是否生效,执行以下命令(替换YOUR_JWT_TOKEN为管理员登录后获取的 token):
curl -X POST http://oj.example.com/api/submissions/ \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "problem_id": 1, "language": "python", "code": "print(\"Hello OJ!\")" }'成功响应包含"status": "pending",10 秒后查GET /api/submissions/1/应返回"status": "accepted"且"time_used_ms": 123。若返回"status": "system_error",检查/var/log/oj/judge.log中是否出现OSError: [Errno 13] Permission denied—— 这表示oj-judge用户无权执行/tmp/下的二进制文件,需执行sudo setfacl -m u:oj-judge:x /tmp/。
5. 生产环境必须启用的 3 项加固措施与监控技巧
5.1 强制 HTTPS 与 HSTS 头注入
在 Nginx 配置的server块中添加:
listen 443 ssl http2; ssl_certificate /etc/letsencrypt/live/oj.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/oj.example.com/privkey.pem; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;同时在settings/production.py中追加:
SECURE_SSL_REDIRECT = True SECURE_HSTS_SECONDS = 31536000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_CONTENT_TYPE_NOSNIFF = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True提示:
SECURE_SSL_REDIRECT = True会使所有 HTTP 请求 301 跳转 HTTPS,但需确保 Let's Encrypt 证书已签发成功,否则网站完全不可访问。首次部署建议先关闭此开关,确认功能正常后再启用。
5.2 PostgreSQL 连接池与慢查询日志
在settings/production.py的DATABASES配置中加入连接池参数:
'OPTIONS': { 'MAX_CONNS': 20, # 最大连接数,匹配 PostgreSQL max_connections * 0.5 'CONN_MAX_AGE': 60, # 连接复用 60 秒 },并在 PostgreSQL 配置/etc/postgresql/*/main/postgresql.conf中启用慢查询:
log_min_duration_statement = 1000 # 记录 >1s 的查询 log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'重启 PostgreSQL 后,慢查询日志将输出至/var/lib/postgresql/data/pg_log/,重点关注SELECT * FROM submission_submission WHERE status = 'pending' ORDER BY created_at LIMIT 100类语句——这是判题队列轮询 SQL,若未在status和created_at上建复合索引,会导致全表扫描。
5.3 判题成功率监控:用 Redis 键值统计异常率
平台未内置监控看板,但可通过 Redis 实时提取关键指标。判题模块在judge_worker.py中写入以下键:
judge:stats:total:累计判题次数(INCR)judge:stats:success:成功次数(INCR)judge:stats:timeout:超时次数(INCR)judge:stats:ce:编译错误次数(INCR)
执行以下命令计算当前成功率:
redis-cli << 'EOF' GET judge:stats:total GET judge:stats:success EOF若success/total < 0.95,需检查judge.log中是否高频出现subprocess.TimeoutExpired—— 此时应调低JUDGE_TIMEOUT或升级服务器 CPU。
注意:Redis 键名前缀
judge:可在judge/config.py中修改,但所有统计脚本需同步更新。不要删除judge:stats:*键,否则统计数据归零。
本文还有配套的精品资源,点击获取