Skip to content

Distributed Judging.zh CN

houyhl edited this page Oct 16, 2026 · 3 revisions

分布式评测与网页分离

English | 简体中文

系统架构与部署结构

推荐的 AI 完整结构:网页节点使用 --role all,保留本机评测和 AI 沙盒 worker;另加远程 judge 分担普通提交评测。纯网页结构:网页节点使用 --role web,普通提交全部由远程 judge 执行,本机不构建 rootfs。

flowchart LR
    Browser[浏览器] --> Nginx[Nginx]
    Nginx --> Frontend[前端资源]
    Nginx --> Backend[后端]
    Backend --> DB[MariaDB]
    Backend --> Redis[Redis]
    Backend --> MinIO[MinIO]
    Judges[本机或远程评测机] <-->|认证的 WebSocket /api/socket| Backend
    Judges --> Sandbox[每台节点的 simple-sandbox 与 rootfs]
    Judges -->|签名下载 /storage/| Nginx
    Backend -->|all 模式:本机 UNIX socket| AIWorker[本机 AI 沙盒 worker]
Loading

judge 主动连接网页节点,不需要网页节点连接评测机的入站端口。远程 judge 不持有数据库、Redis 或 MinIO 管理凭据;它通过后端领取任务,使用短期签名 URL 下载测试文件并缓存,再回传结果。当前部署采用一个后端实例,不是后端数据库的高可用集群。

评测队列

评测任务保存在数据库中(表 judge_job),不只存在于 Redis:提交、Hack 或自测与其评测任务在同一事务中保存,因此重启后端或 Redis 不会丢失排队中的任务。Redis 只负责唤醒等待中的评测机,评测机每隔几秒也会主动查找任务。

  • 每个任务同一时间只交给一个评测连接,并持有租约;评测机保持连接时后端会续租。结果与任务完成一起保存,来自旧租约(例如重连之前)的结果会被忽略。
  • 评测机断开时,其未完成的任务会立即重新排队。后端本身停止时,其租约在 90 秒后过期,由其他后端进程或重启后的后端重新排队。因此一个任务可能被评测不止一次,但其结果只计入一次。
  • 中断 5 次,或准备失败 10 次(例如缺少测试数据)后放弃该任务:对应提交显示 系统错误 及原因,可以重测。
  • 站点管理员可在 评测机 页面(/judge-machine)底部查看队列:等待中和评测中的任务、持有任务的评测机、重试次数,以及近 7 天放弃的任务。指标 libreoj_judge_jobs 报告等待中和评测中的任务数。
  • 每次租约及其结束方式记录在 judge_job_attempt 中。已完成的任务及其记录保留 30 天后删除。

排空、吊销与能力声明

以下操作位于站点管理员的 评测机 页面,每次变更都记入审计日志(judge_client.add、delete、reset_key、drain、resume)。

  • 排空(暂停图标):不再向该评测机发送新任务,它会完成手中的任务。仍在评测时状态显示 排空中,完成后显示 已排空:此时可以停止、更新或调整它,不会中断评测,其他评测机继续处理队列。重新连接或后端重启后仍保持排空,直到点击 恢复(播放图标)。排空不影响本机 AI 验证,它不经过评测队列。
  • 重置密钥 与 删除 立即生效:所有后端进程都会断开使用旧密钥的连接,其未完成的任务重新排队,交给其他评测机。密钥可能泄露时请重置,然后把新密钥配置到评测机上。
  • 能力声明:评测机连接时会声明协议版本、可运行的任务类型、任务线程、执行槽位、AI 验证槽位与内存预算,显示在 容量 一栏。后端只向它发送其声明的任务类型。旧版本评测机不会声明,标记为 旧版:只会收到提交,Hack 与自测会等待已更新的评测机上线。

网页节点

纯网页安装示例,替换仓库与实际域名:

sudo bash /tmp/llmoj-install.zh-CN.sh --repository houyhlouis/LLMOJ \
  --role web --public-url http://oj.example.com

公网远程评测先按 HTTPS 配置最终 HTTPS 地址,或使用受控内网。HTTP 连接会明文传输评测密钥,不适合公网的长期评测连接。

安装器已为 /api/socket 配置 WebSocket Upgrade,并将 services.minio.forJudge.urlEndpoint 设置为最终 origin 下的 /storage/。Nginx 转发存储请求时恢复 MinIO 签名所使用的内部 Host。数据库及 MinIO 本身保持回环监听。

注册每台评测机

管理员通过 POST /api/judgeClient/addJudgeClient 注册客户端,请求体为 {"name":"judge-01","allowedHosts":[]}。

在已登录管理员的站点页面打开浏览器开发者工具 Console,执行下面的请求。它从当前页面状态取得会话,不输出或复制管理员 token:

if (!window._appState?.currentUser?.isAdmin) throw new Error('请先登录管理员');
fetch('/api/judgeClient/addJudgeClient', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + window._appState.token
  },
  body: JSON.stringify({name: 'judge-01', allowedHosts: []})
}).then(response => response.json()).then(result => {
  console.log(result.judgeClient ? '客户端已创建;请从本次响应安全保存 key' : result);
});

在 Network 面板查看该次响应中的 judgeClient.key,仅保存到该评测机的私有 key 文件。每台机器注册不同客户端,使用不同 key;同一 key 的新连接会替换旧会话,不能让多台机器共享一个 key。

当前网关根据 key 认证;虽然 API 保留 allowedHosts 字段,当前连接逻辑不执行该字段的来源 IP 校验。不要把它当作访问控制措施;需要限制来源时在反向代理、VPN 或防火墙层配置。

部署及验收

按 Remote-Judge 部署每台 judge,使用与网页节点相同的代码版本和 rootfs 配方。日志出现“Successfully authorized as …”后,在管理员 Console 查询状态:

fetch('/api/judgeClient/listJudgeClients', {
  headers: {'Authorization': 'Bearer ' + window._appState.token}
}).then(response => response.json()).then(result => {
  console.table(result.judgeClients?.map(({id, name, online}) => ({id, name, online})));
});

提交一个简单 C++ 程序,确认测试文件能下载、编译、运行并得到 Accepted;再验证编译错误、超时、内存超限和取消。多台节点分别验证,重启其中一台观察任务恢复。现有队列可由多个客户端消费,但不能把任务恢复理解为恰好一次执行的保证。

AI 与纯网页限制

模型编辑、翻译、检索等后端 AI 操作可以继续配置。AI 标程验证和测试数据生成目前使用网页服务器上的本机 UNIX socket 与私有共享目录,未实现跨机器的 AI worker 协议。新增远程评测机只分担普通提交,不会迁移这些执行功能;它们需要 all 模式保留的本机 worker,在 web 模式下不可用。不要用开放 TCP 或不受控共享目录替代本机 AI socket。

排错

  • 连接失败:检查最终 origin、TLS、/api/socket Upgrade 和独立 key。
  • 在线但下载失败:检查签名 URL 是否仍指向 127.0.0.1、19000 或不可达域名;核对 /storage/ 转发与签名 Host。
  • 多台互相掉线:检查是否共享了 key。
  • cgroup 错误:检查 systemd 委托、统一 cgroup v2 和宿主内核能力。
  • 增加节点后 AI 执行仍失败:检查网页节点是否保留本机 worker,分布式普通评测不能替代它。
  • Hack 或自测一直等待:确认有一台未排空的在线评测机运行当前版本(不是 旧版),且其 容量 中列出了这些任务类型。
  • 评测机收不到任务:检查它是否已被排空,并恢复它。

尚未完成真实多机端到端验收;部署前应完成上述现场验证。

CPU 与现有节点容量

逐项 YAML 配置、消费者/执行槽/下载队列区别、8 CPU/8 GiB 示例和保留 key 的扩缩容/回滚流程见 评测端配置。先对一台节点维护并验收,再逐台处理;远程节点扩槽不增加网页节点本机 AI worker 的容量。不要改安装 metadata 或以不同 --judge-slots 重跑安装器来代替此流程。

Clone this wiki locally