2018-12-09

新 OJ 技术选型

这篇文章作为 开发 OnlineJudge 啊分类里 的第一篇文章, 将会阐述网站 数据库 Database, 网站后台 Master, 判题 Judger, 运行环境 Server 的技术选型过程.

构架

整个OJ可以分为 关系数据库, 内存数据库, 网站(后期前后端可以再分离), 判题任务队列管理, 判题机, 这五个部分, 每部分都可以部署在同一服务器或这多台服务器上, 必要时可以一个部分部署在多台服务器上

运行流程:

正常情况: 用户提交代码 -> 插入关系数据库 -> 网站通知队列管理 -> 队列管理将任务插入任务队列 -> 队列管理通知某一判题机取任务 -> 判题机请求任务详情(测试数据, 限制) -> 判题机判题 -> 将结果上报 -> 队列管理更新数据库结果 -> 结束

重判题目: 网站通知队列管理 -> 队列管理将大量任务插入任务队列 -> 队列管理通知某一判题机取任务 -> [1]判题机请求任务详情(测试数据, 限制) -> 判题机判题 -> 将结果上报 -> 队列管理更新数据库结果 [1] -> 重复过程[1] 直到队列为空 -> 结束

数据库 Database

数据库 是整个系统中比较重要的部分, 一旦做些选择就尽量不再改动, 如果换数据库会带来(停机)迁移的过程, 这可能会十分麻烦.

关系数据库

在数据量大的时候是否会引起查询过慢, 能否承受大量的数据查找(该字段中含有长文本或条目数较多未建立索引), 代码的存储(由于部分题目为热点题目, 需要比较代码相似度, 若代码只存在于数据库中, 则可能会出现频繁读的情况), 以及由于需要一些额外记录, 需要Json的支持, 这些都是得考虑的东西.

PS. 代码文件也许可以将热点数据放于内存中或者是文件中

数据库大概就两个方向, MySQL 系(MySQL, MariaDB, WebScaleSQL)和 PostgreSQL, MySQL系出现的原因是因为 MySQL 被Oracle收购后, 人们害怕 MySQL 闭源, 便根据现有代码继续开发; MySQL 号称是世界上最受欢迎的数据库, PostgreSQL 号称是世界上最好用的数据库, 这两个数据库都有大量的厂商在使用, 当然各大是根据系统需求而选择不同数据库, 而不是只使用一种(Amazon 还用Oracle呢, 说是在去O了).

在Ubuntu 18.04 的仓库中 PgSql和MySQL的版本都不是最新, 而是 10 与 5.7, 但在Docker Hub中Latest都是最新的版本 11 和 8(5.8). 参考了 PostgreSQL vs MySQL 2018, Showdown: MySQL 8 vs PostgreSQL 10, System Properties Comparison MySQL vs. PostgreSQL, SQL compliance, https://stackshare.io/stackups/mysql-vs-postgresql, MySQL vs. PostgreSQL vs. MariaDB 这些文章.

比较过程

之前MySQL最令人诟病的就是默认使用 MyISAM 这个不支持事务的引擎, 但在 5.6 版本出来后, 创建表默认都使用 InnoDB 引擎, 这跟PgSQL是相同的.
PgSQL 在文本类型上并不会支持太复杂, 对于长文本只有 text 一种类型, 但MySQL则分成多种类型, 比较繁琐, 对于其他的类型, 有部分还是不支持, 但这部分并没有使用的需求.
MySQL5.7 开始 跟PgSQL一样支持了Json, 从8开始支持了GIS.
MySQL 不支持约束检查和部分索引, 但PgSQL支持.
MySQL 所支持的 SQL 标准比 PgSQL 要少的多, 但业务中使用CUR居多, 对支持SQL规范程度没有没事要求.
PgSQL 支持多进程, MySQL 不支持
PgSQL 需要定时执行VACUUM命令以回收无用的块, 这个是比较占用资源的, 但可以配置为自动清除, 清除过程中会对CURD操作有影响, MySQL 的回收无用的块在别的线程中, 但不会影响正常的操作. 业务中并没有太多的Delete操作.
业务中多为CRU操作, PgSQL在更新上的速度会慢一些,它会将未改动的数据复制一份作为新数据的一部分, 旧数据则在原处存放直至VACUMM 也就是说回滚会快一些, 这跟其储存设计有关; 但MySQL则相反, 更新操作能立刻完成, 但回滚会相对慢一些.
热度来说 MySQL的使用者是大于PgSQL的, 毕竟LAMP中的M就是MySQL, 但PgSQL还是在不断的增长中.
在社区方面, MySQL的帮助会比PgSQL多, 毕竟用的人多, 但MySQL 5.7 居然没有一份中文文档, 而PgSQL 10有中文文档.

综上, 考虑到现有系统中存在大量的更新存在, 如果使用PgSQL将会在运行时引起性能的问题, 所以最后选用 MySQL 作为关系数据库

内存数据库

内存数据库主要用于热点数据缓存, 避免大量文件的存在拖垮系统磁盘, 也能增加运行速度, 常见的有 Memcached , Redis, MySQL(嗯, 他家也有内存数据库引擎), 但考虑到我们使用的需求, 用于缓存 常被访问的代码 系统配置 以及其他一些例如 Session 之类的, 键值对存储数据库会比较适合我们, 再依照之前的使用过的体验, 还是选择 Redis.

Redis 是 键值对存储数据库, 支持持久化储存, 主从配置, 还能实现 哈希 集合 队列 栈 这些功能, 开发上借鉴了 Memcached, 支持过期清除; 使用也简单方便, 综合这些方面选择了 Redis 作为内存数据库.

开发框架

一个合适的框架将会减轻阅读代码的难度以及上手项目的难度, 由于考虑到学习程度以及适用性, 在NodeJS, PHP, Go中还是选择了 PHP 作为网站后端的运行语言, 如果需要前后端分离则使用NodeJS的Vue(不接受论证)

PHP框架

PHP的MVC框架大多都是遵守了PSR-4自动加载, 找了找 比较流行的还仍在维护的口碑比较好的开发便捷的原生PHP框架,还有这么些 laravel, ThinkPHP5, CakePHP, Yii, Slim.

有完全中文文档的只有 Yii, ThinkPHP, Laravel. ThinkPHP是国人写的框架, 而其他两个是外国人写的. Laravel 可以说是十分热门, 在Google上搜索PHP MVC, 随便打开一个页面都会有它, 之前使用过ThinkPHP 3.2版本, 觉得上手速度也挺快的, 但ThinkPHP5 就觉得不怎么样了, Yii 这个框架, 没有使用过, 也没有什么体验.

本着用新不用旧的原则, 当然是在Yii和Laravel中选一个, 就分别看了文档, 搞了个小玩意出来.

看文档的时候, 觉得 Laravel 的文档比 Yii 难理解; 于是上官网翻了翻, 找到了个 https://laracasts.com/, 上面的内容挺多的, 有包括PHP Laravel Vue在内的教程, 看了几篇后觉得Laravel 代码写的挺简洁的, 相比之下 Yii 的的框架比较啰嗦. 以及我觉得 Laravel 的Model方面处理得比较好, Laravel的用户认证方面做的算是开箱就能用了, 所以能减轻一定的开发负担, 还引入了一定的中间件, 和原生支持Redis,所以我最终选择了Laravel作为新OJ后端的开发框架, 同时, 判题调度也将借助Laravel的定时计划实现.

前端框架

CSS 框架本想采用 Bootstarp 的, 但后来发现 Bulma 这个框架, 对比之后觉得 Bulma 不需要使用 JQuery 会使得网页轻便一些, 毕竟之后会引入 MathJax, PrimeJs 等Js脚本, 希望网页加载少一些内容.
JS 框架: Vue, 但暂时不做前后端分离, 只会将动态展示交给 JS 去做, 我们追求尽量快的前端响应. 如果之后开始前后端分离, 我也会考虑直接使用Vue.

那么这样一来, 我们只能支持 IE10+ 和其它现代浏览器(古老的IE).

Judger

Judger 作为判题机的重要组成部分, 在 Python 和 Go 之间选择后, 我们选择了Go这个编译型,高并发, 与C风格相似, 具有垃圾回收功能的语言作为 Judger 的守护程序的编写语言. Judger 需要一个沙箱来安全的运行用户提交的代码, 由于是在Linux下使用, 只能是借助于Linux内核的 ulimit, seccomp 来限制对程序的资源和系统调用限制.

关于代码查重的方面, 则需要将 对应的功能组件放在 有数据库访问权限的 服务器/容器 上, 通过监听请求拉取过往AC代码进行查重, 为了减轻数据库负担, 将会使用 redis 将热点数据缓存.

运行环境

Linux, Windows 下 沙箱开发将会是个巨大的工程. 故所有的项目将会放在 Linux 下开发, 测试, 运行.

Last

所有的东西将会开源放在 github 下, 亦或者 git.boxjan.li下(开发过程中), 大约都会使用 MIT 作为开源协议.

-- EOF --

comments

如果无法加载 请将 disqus.com | disquscdn.com 加入代理