设计一个OJ

0x01 据说国内各大学学习编程用的OnlineJudge(简称OJ)大都是学生(大三四)编写的,常常因为学生毕业而不再有人维护,外加各种考虑不周导致OJ稳定性不好…诸如考着试服务器挂了,老师一脸懵逼的事情都略有耳闻。于是就有目前国内比较著名的OJ只有杭电和北大。

0x02 通常考虑:传统网站(如PHP)没多一个用户访问,服务器就会多开一个php进程,当同时访问的人一多,服务器内存/CPU开销就大,速度变慢,再多可能就崩溃了。对于普通的网页处理各种服务都做了不少优化,让它负载能力还可以,但是对于OJ系统,它需要执行GCC等编译器编译代码,另外根据题目需要可能还会有各种递归/动态规划等大CPU/内存占用的程序需要被运行,再比个赛/考个试几十个这样的程序同时跑,这无疑让低配置(乃至高配)的服务器难以承受。故在此写些从其他OJ上的获得的灵感,仅供参考。

0x03 首先,至少需要一台能跑网页的服务器,一个数据库,以及一台及以上台可以评测程序的机器,当然以上三个可以是一台机器。其次,写一个用户管理系统&会话(token)系统以及用户界面(UI)、题目呈现、上传源程序等OJ基本功能,这些都烂大街了也不是重点,略。后面重点说评测。

0x04 第一步处理提交。用队列思想。用户提交程序后先将源程序找个地方存好,INSERT一条数据进一张表(例如’Submit’),包含 任务ID、提交用户、提交时间、提交目标(哪个比赛/题目)、源程序内容/路径、状态(等待/排队中/评测中/结果) 等内容。INSERT后触发一个触发器,召唤”分工者”。

0x05 “分工者”(Manager,拒绝多进程?)被激活后会SELECT出所有状态是”等待”的任务,按负载均衡原则分发(INSERT)到N张表(例如’JudgementN’,N是可用的评测机器/进程数),内容至少包含’Submit’中的’任务ID’,并将这些任务的状态改成”排队”。INSERT后,每张表触发一个触发器,召唤各自的评测进程(Judgement)。”分工者”发现’Submit’表不再有”等待”的任务后,进入休眠(结束)。

0x06 评测进程是非web进程,对应一张表只允许一个进程。评测进程被激活后,从对应的表(JudgementN)里按(ID≈时间)顺序编译&运行&评测,并将需要的信息/结果UPDATE到’Submit’表对应的ID里,同时在’JudgementN’里将该任务删除/标记已测。评测进程发现自己的表里没有需要执行的任务后,进入休眠(结束)。

0x07 至此,评测功能基本完成,这样设计应该能保证同一时间评测进程的数量不超,(不考虑web服务对服务器的压力的话)服务器能应对大批量评测。用户查询结果尽管SELECT ‘Submit’表。若仅有一台评测机(一个评测进程),那么所有人提交都按提交顺序进行,对于多个机器/进程,那么后提交有可能会先评测。

0x08 反思。你说队列模式提交的人一多就得等待很久?提高服务器配置是王道,总比崩溃强。

0x09 思考。何不取消”分工者”,让评测进程自己去’Submit’表找任务?似乎效率更高?考虑到可能出现两个评测进程同时访问可能出现同时评测同一任务(并行的缺陷?)故作”分工者”设计,如果读者有兴趣可以分别测试一下两种的效率然后告诉我。

0x0A 网友建议,若web和评测进程在同一台机器上,在web压力大时(例如考试结束前几分钟),”分工者”可以控制停止评测进程(同时更新Submit表对应任务的状态),让web服务使用更多资源。

0x0B 运用nodejs解决web高并发量问题?

0x0C “分工者”应及时均衡负载,例如在临时添加/减少评测进程时,或者某评测进程测试某些耗时程序而其他评测进程空闲时,及时把改进程的任务队列分发到其他评测进程…以维持”先交先测”。#(捂脸)似乎不要”分工者”还好…

“设计一个OJ”的一个回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注