refactor(判题机): 换成 go-judge,空闲内存从几百 MB 降到 16 MB

原来的 JudgeServer 按核数起 gunicorn worker、每份提交再按核数开进程池,空闲内存跟着核数走
(2 核 52MB、16 核 305MB)。换成 go-judge 后空闲 16MB,同一批提交判题中 80–100MB(原来约 370MB)。

go-judge 只管在沙箱里跑命令,编译、逐点运行、比对输出、定状态码搬到 judge/sandbox.ts,
口径逐条照搬 JudgeServer,出参形状不变。拿生产库真实提交新旧逐点对比:C 2000 份 1989 一致、
C++ 891 份 890 一致、Python 2000 份全一致;不一致的是 system("pause")(现在不再判运行时错误)、
未定义行为、endl 死循环打印(运行时错误变运行超时),见 docker/judge/README.md。

- 判题容器改成 privileged(go-judge 要自己建 cgroup),镜像 oj2-judge-3
- 心跳改由 worker 每 5 秒问判题机 /version 代记,删掉 /api/judge-server/heartbeat
- 前端统一用 stripJudgePath 去掉判题机工作目录(新的 /w/ 和旧的 /judger/run/)
- 提交前编译检查只编译不运行

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-26 15:03:57 +08:00
1 parent 8427475337
commit 37e971b6b4
30 files changed
+881 -592

No files matched your search

+9 -21
View File
@@ -21,9 +21,9 @@
# echo "OJ2_JUDGE_TOKEN=$TOKEN" >> docker/.env # docker compose 读这里
# echo "JUDGE_SERVER_TOKEN=$TOKEN" >> .env # 后端读这里,两边必须一致
#
# 两个变量名不同但值必须相同:TOKEN 是判题机镜像认的名字,JUDGE_SERVER_TOKEN 是后端认的名字。
# 忘了设 OJ2_JUDGE_TOKEN 的话 docker compose 会直接报错退出;忘了设 JUDGE_SERVER_TOKEN
# 的话后端会随机生成一个并在启动日志里告警,判题机心跳会被 403 挡掉。
# 两个变量名不同但值必须相同:ES_AUTH_TOKEN 是判题机(go-judge)认的名字,JUDGE_SERVER_TOKEN
# 是后端认的名字。忘了设 OJ2_JUDGE_TOKEN 的话 docker compose 会直接报错退出;忘了设
# JUDGE_SERVER_TOKEN 的话后端会随机生成一个并在启动日志里告警,判题请求会被 401 挡掉。
services:
postgres:
@@ -60,37 +60,25 @@ services:
retries: 10
judge:
# 自己构建的判题沙箱(上游 JudgeServer 1.6.1 + 新工具链,见 docker/judge/)。
# 上游停更在 2024-04,registry 上的 1.6.1 == latest,没有新版可拉。
# 自己构建的判题沙箱(go-judge + gcc / python,见 docker/judge/)。
# ⚠️ 这个 tag 不在任何 registry 上:本机 docker/judge/build.sh 构建,
# 服务器/机房用 docker load 装。镜像不在本地时 compose 会去 pull 然后报找不到。
image: oj2-judge-2
image: oj2-judge-3
container_name: oj2-judge
restart: unless-stopped
read_only: true
cap_drop:
- SETPCAP
- MKNOD
- NET_BIND_SERVICE
- SYS_CHROOT
- SETFCAP
- FSETID
# go-judge 要自己建 namespace 和 cgroup 来关学生程序,只能给特权,理由见 docker/judge/README.md
privileged: true
# go-judge 的文件存储(编译产物、程序输出)放这里,见 Dockerfile 的 -dir
tmpfs:
- /tmp
ports:
- "127.0.0.1:8081:8080"
volumes:
- ../data/test_case:/test_case:ro
- ../data/judge_server/log:/log
- ../data/judge_server/run:/judger
environment:
SERVICE_URL: http://oj2-judge:8080
BACKEND_URL: http://host.docker.internal:3000/api/judge-server/heartbeat
# 没有默认值是故意的:写死在仓库里的弱 token 等于没有 token。
# 未设置时 docker compose 直接报错,不会静默起一个人人可连的判题机。
TOKEN: ${OJ2_JUDGE_TOKEN:?请先设置 OJ2_JUDGE_TOKEN,见本文件顶部注释}
extra_hosts:
- "host.docker.internal:host-gateway"
ES_AUTH_TOKEN: ${OJ2_JUDGE_TOKEN:?请先设置 OJ2_JUDGE_TOKEN,见本文件顶部注释}
mem_limit: 512m
volumes: