边搭边理解 OJ 是如何写成的
什么是 OJ
OJ 是 Online Judge(在线判题系统)的缩写,通常用于算法代码测试、算法竞赛等场景中,主要包含一个相对简单纯粹的代码运行沙盒环境、与用户交互的在线界面以及运行任务的管理调度等功能。国内比较重视算法竞赛(ACM)的学校都有自己的 OJ,比如有名的 杭州电子科大 OJ、北京大学 OJ、青岛大学 OJ、上海大学 OJ等等。除此之外,国内外也有不少企业专注于给大家提供一个在线判题平台,比如有名的 LeetCode、LeetCode CN、牛客网、DMOJ 等等。其实如果只是想练习算法,这些 OJ 系统是非常合适的。只要在具有一定名气的 OJ 上一直刷题,最后找算法岗工作的时候还是很有用的。
为什么选 DMOJ
OJ 系统中有一部分是开源的,比如青岛大学OJ、DMOJ等等。如果想要搭建一个自己玩一玩,可以选择青岛大学 OJ,毕竟官方提供了一步到位的 docker-compose 方式安装。只要有 Docker 环境就可以从 docker-compose.yml 启动一套完整的实例。这样的搭建的确是简单易用,但是也失去了从头开始一步一步部署了解 OJ 是如何构成、运行的机会。除了这之外,还有一个非常重要的原因。大部分的开源 OJ 支持的编程语言是相当少的,基本上是在 C
、C++
、Java
、Python
范围内,少数像 LeetCode 这样的 OJ 支持 Javascript
、Go
等更多种常用编程语言。而 DMOJ 号称可以支持超过60种编程语言,这简直是囊括了绝大多数见过的编程语言了。所以,我最终选择了 DMOJ 来做这次实践学习。
安装 DMOJ
DMOJ 官方提供的 文档 大致上写得还是比较细致的,所有的文件配置关联与应用启动都在合适的时候进行修改,对于通过 DMOJ 来了解 OJ 的构成与运行非常有帮助。在按照文档安装的过程中,也发现了一些小问题,并通过查找其他的资料一步步解决。以下就让我们来试着完整的安装一次吧。
安装环境
- Ubuntu Server 18.04 操作系统
正式安装前的准备工作
- 安装基本软件和编译环境
由于 DMOJ 是基于 Django 框架和 NodeJS 运行的,因此需要预先安装 Python 和 NodeJS。再者,在代码运行任务调度中需要有消息队列来缓冲提交的任务,所以预先安装 Redis 数据库。
sudo apt update && sudo apt upgrade -y
sudo apt install git gcc g++ make python3-dev python3-pip libxml2-dev libxslt1-dev zlib1g-dev gettext curl redis-server -y
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt install nodejs
sudo npm install -g sass postcss-cli autoprefixer
- 安装和配置数据库
这里所采用的是 Mariadb 数据库,Ubuntu 安装极为方便。由于现在的 Mariadb 数据库安装完后不会要求用户设置 root 密码,直接在本地登录密码默认为空。
sudo apt update
sudo apt install mariadb-server libmysqlclient-dev -y
sudo mysql -u root -p
mariadb> CREATE DATABASE dmoj DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;
mariadb> GRANT ALL PRIVILEGES ON dmoj.* to 'dmoj'@'localhost' IDENTIFIED BY '<password>';
mariadb> exit
由于 DMOJ 设计时数据库用了较长的索引,此处还需要有进一步对数据库进行设置(官方文档中没有提到,可能是操作系统不同的原因)。在 /etc/mysql/mariadb.conf.d/50-server.conf 文件的 innodb 注释位置添加如下三行内容,修改完成后重启 Mysql 数据库生效配置。除此在外,还要在之后下载的 DMOJ 主项目代码文件 site/manage.py 中添加如下两行。
[mysqld]
...
innodb_file_format = Barracuda
innodb_file_per_table = 1
innodb_large_prefix
#重启Mysql数据库
sudo service mysql restart
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sampleproj.settings")
# 增加下面两行
from django.db.backends.mysql.schema import DatabaseSchemaEditor
DatabaseSchemaEditor.sql_create_table += " ROW_FORMAT=DYNAMIC"
- 安装 python 沙盒工具
为了使本项目运行环境与操作系统中的其他 Python 项目环境不相互干扰,采用了 venv 沙盒工具。不仅在调试的工作中使用,在部署生产的时候也将采用 venv 沙盒工具。此处三条命令执行完就进入了沙盒模式,在终端提示的最前面会出现 (dmojsite) 的提示。
sudo apt install -y python3-venv
python3 -m venv dmojsite
. dmojsite/bin/activate
开始安装DMOJ
- 下载代码并安装依赖
这一步骤就是从 Github 下载 DMOJ 的主项目代码到本地,并添加子项目的 git 追踪和更新代码。在这里值得注意的是,如果决定之后采用 Docker 方式配置判定服务器的话,就不需要切换分支。如果不然,则必须要切换分支。
git clone https://github.com/DMOJ/site.git
cd site
git checkout v2.1.0 # 这一步只在后面使用pypi方式配置判定服务器的时候需要执行
git submodule init
git submodule update
pip3 install -r requirements.txt
pip3 install mysqlclient
- 修改项目配置
这个步骤是非常重要的,决定了主项目代码是否能正常运行。可以从官方提供的 下载地址 获取基本文件,也可以直接使用我提供的配置文件 local_setting.py,内容如下。
#####################################
########## Django settings ##########
#####################################
# See <https://docs.djangoproject.com/en/1.11/ref/settings/>
# for more info and help. If you are stuck, you can try Googling about
# Django - many of these settings below have external documentation about them.
#
# The settings listed here are of special interest in configuring the site.
# SECURITY WARNING: keep the secret key used in production secret!
# You may use <http://www.miniwebtool.com/django-secret-key-generator/>
# to generate this key.
# 访问前两行的URL生成一个key
SECRET_KEY = 'anpsa4ko6^@b5u-)e9gm$vk(=lqb()-%0n@lr5c^=$feq45jdh'
# SECURITY WARNING: don't run with debug turned on in production!
# 生产环境下一定要修改成False
DEBUG = False # Change to False once you are done with runserver testing.
# Uncomment and set to the domain names this site is intended to serve.
# You must do this once you set DEBUG to False.
ALLOWED_HOSTS = ['10.0.4.9','oj.lep.ac.cn'] #设置成允许访问的ip或域名
# Optional apps that DMOJ can make use of.
INSTALLED_APPS += (
)
# Caching. You can use memcached or redis instead.
# Documentation: <https://docs.djangoproject.com/en/1.11/topics/cache/>
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
}
}
# Your database credentials. Only MySQL is supported by DMOJ.
# Documentation: <https://docs.djangoproject.com/en/1.11/ref/databases/>
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dmoj',
'USER': 'dmoj',
'PASSWORD': '<password>', #需要修改为自己设定的
'HOST': '127.0.0.1',
'OPTIONS': {
'charset': 'utf8mb4',
'sql_mode': 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION',
},
}
}
# Sessions.
# Documentation: <https://docs.djangoproject.com/en/1.11/topics/http/sessions/>
#SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
# Internationalization.
# Documentation: <https://docs.djangoproject.com/en/1.11/topics/i18n/>
LANGUAGE_CODE = 'zh-hans' # 可以修改成符合国际规范的语言编码
DEFAULT_USER_TIME_ZONE = 'Asia/Shanghai' # 设置时区
USE_I18N = True
USE_L10N = True
USE_TZ = True
## django-compressor settings, for speeding up page load times by minifying CSS and JavaScript files.
# Documentation: https://django-compressor.readthedocs.io/en/latest/
COMPRESS_OUTPUT_DIR = 'cache'
COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.cssmin.CSSMinFilter',
]
COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.JSMinFilter']
COMPRESS_STORAGE = 'compressor.storage.GzipCompressorFileStorage'
STATICFILES_FINDERS += ('compressor.finders.CompressorFinder',)
#########################################
########## Email configuration ##########
#########################################
# See <https://docs.djangoproject.com/en/1.11/topics/email/#email-backends>
# for more documentation. You should follow the information there to define
# your email settings.
# Use this if you are just testing.
#EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# The following block is included for your convenience, if you want
# to use Gmail.
#EMAIL_USE_TLS = True
#EMAIL_HOST = 'smtp.gmail.com'
#EMAIL_HOST_USER = '<your account>@gmail.com'
#EMAIL_HOST_PASSWORD = '<your password>'
#EMAIL_PORT = 587
# To use Mailgun, uncomment this block.
# You will need to run `pip install django-mailgun` for to get `MailgunBackend`.
#EMAIL_BACKEND = 'django_mailgun.MailgunBackend'
#MAILGUN_ACCESS_KEY = '<your Mailgun access key>'
#MAILGUN_SERVER_NAME = '<your Mailgun domain>'
# You can also use Sendgrid, with `pip install sendgrid-django`.
#EMAIL_BACKEND = 'sgbackend.SendGridBackend'
#SENDGRID_API_KEY = '<Your SendGrid API Key>'
# The DMOJ site is able to notify administrators of errors via email,
# if configured as shown below.
# A tuple of (name, email) pairs that specifies those who will be mailed
# when the server experiences an error when DEBUG = False.
ADMINS = (
('zhonger', 'zhonger@lep.ac.cn'),
)
# The sender for the aforementioned emails.
SERVER_EMAIL = 'LEPOJ: Modern Online Judge <zhonger@lep.ac.cn>'
##################################################
########### Static files configuration. ##########
##################################################
# See <https://docs.djangoproject.com/en/1.11/howto/static-files/>.
# Change this to somewhere more permanent., especially if you are using a
# webserver to serve the static files. This is the directory where all the
# static files DMOJ uses will be collected to.
# You must configure your webserver to serve this directory as /static/ in production.
# 这个目录是用于存放静态文件的地方
STATIC_ROOT = '/tmp/static/'
# URL to access static files.
#STATIC_URL = '/static/'
#STATICFILES_DIRS = (
# os.path.join(BASE_DIR, "static"),
#)
# Uncomment to use hashed filenames with the cache framework.
#STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.CachedStaticFilesStorage'
############################################
########## DMOJ-specific settings ##########
############################################
## DMOJ site display settings.
SITE_NAME = 'LEPOJ'
SITE_LONG_NAME = 'LEPOJ: Modern Online Judge'
SITE_ADMIN_EMAIL = 'zhonger@lep.ac.cn'
TERMS_OF_SERVICE_URL = '//oj.lep.ac.cn/tos' # Use a flatpage.
## Bridge controls.
# The judge connection address and port; where the judges will connect to the site.
# You should change this to something your judges can actually connect to
# (e.g., a port that is unused and unblocked by a firewall).
BRIDGED_JUDGE_ADDRESS = [('localhost', 9999)] # 任务调度服务配置
# The bridged daemon bind address and port to communicate with the site.
#BRIDGED_DJANGO_ADDRESS = [('localhost', 9998)]
## DMOJ features.
# Set to True to enable full-text searching for problems.
ENABLE_FTS = True
# Set of email providers to ban when a user registers, e.g., {'throwawaymail.com'}.
BAD_MAIL_PROVIDERS = set()
# The number of submissions that a staff user can rejudge at once without
# requiring the permission 'Rejudge a lot of submissions'.
# Uncomment to change the submission limit.
#DMOJ_SUBMISSIONS_REJUDGE_LIMIT = 10
## Event server.
# Uncomment to enable live updating.
#EVENT_DAEMON_USE = True
# Uncomment this section to use websocket/daemon.js included in the site.
#EVENT_DAEMON_POST = '<ws:// URL to post to>'
# If you are using the defaults from the guide, it is this:
EVENT_DAEMON_POST = 'ws://127.0.0.1:15101/' # 提交事件
# These are the publicly accessed interface configurations.
# They should match those used by the script.
#EVENT_DAEMON_GET = '<public ws:// URL for clients>'
#EVENT_DAEMON_GET_SSL = '<public wss:// URL for clients>'
#EVENT_DAEMON_POLL = '<public URL to access the HTTP long polling of event server>'
# i.e. the path to /channels/ exposed by the daemon, through whatever proxy setup you have.
# Using our standard nginx configuration, these should be.
EVENT_DAEMON_GET = 'ws://127.0.0.1:15100/event/' # 获取事件
#EVENT_DAEMON_GET_SSL = 'wss://<your domain>/event/' # Optional
EVENT_DAEMON_POLL = '/channels/' # 频道广播
# If you would like to use the AMQP-based event server from <https://github.com/DMOJ/event-server>,
# uncomment this section instead. This is more involved, and recommended to be done
# only after you have a working event server.
#EVENT_DAEMON_AMQP = '<amqp:// URL to connect to, including username and password>'
#EVENT_DAEMON_AMQP_EXCHANGE = '<AMQP exchange to use>'
## Celery 消息队列服务,需将注释去除
CELERY_BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
## CDN control.
# Base URL for a copy of ace editor.
# Should contain ace.js, along with mode-*.js.
ACE_URL = '//cdnjs.cloudflare.com/ajax/libs/ace/1.2.3/'
JQUERY_JS = '//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js'
SELECT2_JS_URL = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
SELECT2_CSS_URL = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css'
# A map of Earth in Equirectangular projection, for timezone selection.
# Please try not to hotlink this poor site.
TIMEZONE_MAP = 'http://naturalearth.springercarto.com/ne3_data/8192/textures/3_no_ice_clouds_8k.jpg'
## Camo (https://github.com/atmos/camo) usage.
#DMOJ_CAMO_URL = "<URL to your camo install>"
#DMOJ_CAMO_KEY = "<The CAMO_KEY environmental variable you used>"
# Domains to exclude from being camo'd.
#DMOJ_CAMO_EXCLUDE = ("https://dmoj.ml", "https://dmoj.ca")
# Set to True to use https when dealing with protocol-relative URLs.
# See <http://www.paulirish.com/2010/the-protocol-relative-url/> for what they are.
#DMOJ_CAMO_HTTPS = False
# HTTPS level. Affects <link rel='canonical'> elements generated.
# Set to 0 to make http URLs canonical.
# Set to 1 to make the currently used protocol canonical.
# Set to 2 to make https URLs canonical.
#DMOJ_HTTPS = 0
## PDF rendering settings.
# Directory to cache the PDF.
#DMOJ_PDF_PROBLEM_CACHE = '/home/dmoj-uwsgi/pdfcache'
# Path to use for nginx's X-Accel-Redirect feature.
# Should be an internal location mapped to the above directory.
#DMOJ_PDF_PROBLEM_INTERNAL = '/pdfcache'
# Enable Selenium PDF generation
#USE_SELENIUM = True
## Data download settings.
# Uncomment to allow users to download their data
#DMOJ_USER_DATA_DOWNLOAD = True
# Directory to cache user data downloads.
# It is the administrator's responsibility to clean up old files.
#DMOJ_USER_DATA_CACHE = '/home/dmoj-uwsgi/datacache'
# Path to use for nginx's X-Accel-Redirect feature.
# Should be an internal location mapped to the above directory.
#DMOJ_USER_DATA_INTERNAL = '/datacache'
# How often a user can download their data.
#DMOJ_USER_DATA_DOWNLOAD_RATELIMIT = datetime.timedelta(days=1)
## ======== Logging Settings ========
# Documentation: https://docs.djangoproject.com/en/1.9/ref/settings/#logging
# https://docs.python.org/2/library/logging.config.html#logging-config-dictschema
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'file': {
'format': '%(levelname)s %(asctime)s %(module)s %(message)s',
},
'simple': {
'format': '%(levelname)s %(message)s',
},
},
'handlers': {
# You may use this handler as example for logging to other files..
'bridge': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '<desired bridge log path>',
'maxBytes': 10 * 1024 * 1024,
'backupCount': 10,
'formatter': 'file',
},
'mail_admins': {
'level': 'ERROR',
'class': 'dmoj.throttle_mail.ThrottledEmailHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'file',
},
},
'loggers': {
# Site 500 error mails.
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
# Judging logs as received by bridged.
'judge.bridge': {
'handlers': ['bridge', 'mail_admins'],
'level': 'INFO',
'propagate': True,
},
# Catch all log to stderr.
'': {
'handlers': ['console'],
},
# Other loggers of interest. Configure at will.
# - judge.user: logs naughty user behaviours.
# - judge.problem.pdf: PDF generation log.
# - judge.html: HTML parsing errors when processing problem statements etc.
# - judge.mail.activate: logs for the reply to activate feature.
# - event_socket_server
},
}
## ======== Integration Settings ========
## Python Social Auth
# Documentation: https://python-social-auth.readthedocs.io/en/latest/
# You can define these to enable authentication through the following services.
#SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
#SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
#SOCIAL_AUTH_FACEBOOK_KEY = ''
#SOCIAL_AUTH_FACEBOOK_SECRET = ''
#SOCIAL_AUTH_GITHUB_SECURE_KEY = ''
#SOCIAL_AUTH_GITHUB_SECURE_SECRET = ''
#SOCIAL_AUTH_DROPBOX_OAUTH2_KEY = ''
#SOCIAL_AUTH_DROPBOX_OAUTH2_SECRET = ''
## ======== Custom Configuration ========
# You may add whatever django configuration you would like here.
# Do try to keep it separate so you can quickly patch in new settings.
在这一步,只需要修改应用秘钥、数据库密码、语言、时区、站点基本信息,其他中文标注地方可以在后续步骤进行修改。修改完成后,执行以下命令进行验证。
python3 manage.py check # 如使用本人提供配置文件无须此步骤
- 生成静态文件
这一步骤会在/tmp/static/
目录生成并优化项目需要的静态文件。
./make_style.sh
python3 manage.py collectstatic
python3 manage.py compilemessages
python3 manage.py compilejsi18n
- 导入数据库表
# 迁移所有表
python3 manage.py migrate
# 导入测试数据和配置
python3 manage.py loaddata navbar
python3 manage.py loaddata language_small
python3 manage.py loaddata demo
# 创建管理员用户,需输入用户名、邮箱和密码
python3 manage.py createsuperuser
- 创建Celery任务队列
# 启动redis
sudo service redis-server start
#将项目配置文件中Celery配置去除注释使其生效
# 测试运行主项目代码
python3 manage.py runserver 0.0.0.0:8000
# 运行上一步成功后,运行调度程序,十秒内无任何回显则ctrl+c中止
python3 manage.py runbridged
# 运行Celery任务队列,无错误回显即可
pip3 install redis
celery -A dmoj_celery worker
- 配置 uwsgi
众所周知,uwsgi 是一个用于长久运行 Python Web 项目的工具。这里的配置文件放在site
目录下,官方下载地址,也可以用我提供的配置文件。
[uwsgi]
# Socket and pid file location/permission.
uwsgi-socket = /tmp/dmoj-site.sock
chmod-socket = 666
pidfile = /tmp/dmoj-site.pid
# You should create an account dedicated to running dmoj under uwsgi.
# 为了避免因用户权限和文件夹权限导致的运行失败,这里均设置为初始用户
uid = ubuntu
gid = ubuntu
# Paths. 此处的三个目录需修改为对应目录
chdir = /home/ubuntu/site/
pythonpath = /home/ubuntu/site/
virtualenv = /home/ubuntu/dmojsite/
# Details regarding DMOJ application.
protocol = uwsgi
master = true
env = DJANGO_SETTINGS_MODULE=dmoj.settings
module = dmoj.wsgi:application
optimize = 2
# Scaling settings. Tune as you like.
memory-report = true
cheaper-algo = backlog
cheaper = 3
cheaper-initial = 5
cheaper-step = 1
cheaper-rss-limit-soft = 201326592
cheaper-rss-limit-hard = 234881024
workers = 7
# 安装依赖
pip3 install uwsgi
# 测试配置文件是否有效
uwsgi --ini uwsgi.ini
此处查看执行命令的回显,未报错误信息并正常启动 worker 即可。验证是否完全有效需要等后续配置 nginx。
- supervisord 管理
其实以上步骤已经对 DMOJ 中非常重要的几个部分完成了部署,包括数据库 Mysql、主项目代码运行 uwsgi、任务队列 Celery、任务调度 Bridged。但是除了数据库是时刻保持在后台的服务以外,其他三项均是前台执行,因此需要用 supervisord 来给他们后台化。
# 安装 supervisord
sudo apt install supervisor -y
安装完成后将以下三个配置文件放在 /etc/supervisor/conf.d/ 文件目录下。为了避免因用户权限和文件夹权限导致的运行失败,这里均设置为初始用户执行程序。
# site.conf
[program:site]
command=/home/ubuntu/dmojsite/bin/uwsgi --ini uwsgi.ini
directory=/home/ubuntu/site/
stopsignal=QUIT
stdout_logfile=/tmp/site.stdout.log
stderr_logfile=/tmp/site.stderr.log
# bridged.conf
[program:bridged]
command=/home/ubuntu/dmojsite/bin/python manage.py runbridged
directory=/home/ubuntu/site/
stopsignal=INT
# You should create a dedicated user for the bridged to run under.
user=ubuntu
group=ubuntu
stdout_logfile=/tmp/bridge.stdout.log
stderr_logfile=/tmp/bridge.stderr.log
# celery.conf
[program:celery]
command=/home/ubuntu/dmojsite/bin/celery -A dmoj_celery worker
directory=/home/ubuntu/site/
# You should create a dedicated user for celery to run under.
user=ubuntu
group=ubuntu
stdout_logfile=/tmp/celery.stdout.log
stderr_logfile=/tmp/celery.stderr.log
# 更新supervisord监控列表并查询状态,但均为running是正常运行
sudo supervisorctl update
sudo supervisorctl status
- 安装并配置 Nginx
# 安装nginx
sudo apt install nginx -y
将 /etc/nginx/sites-available/default 删除并新建新的同名文件,填入以下内容。
server {
listen 80;
listen [::]:80;
# Change port to 443 and do the nginx ssl stuff if you want it.
# Change server name to the HTTP hostname you are using.
# You may also make this the default server by listening with default_server,
# if you disable the default nginx server declared.
server_name oj.lep.ac.cn;
add_header X-UA-Compatible "IE=Edge,chrome=1";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
charset utf-8;
try_files $uri @icons;
error_page 502 504 /502.html;
location ~ ^/502\.html$|^/logo\.png$|^/robots\.txt$ {
root /home/ubuntu/site/;
}
location @icons {
root /home/ubuntu/site/resources/icons;
error_page 403 = @uwsgi;
error_page 404 = @uwsgi;
}
location @uwsgi {
uwsgi_read_timeout 600;
# Change this path if you did so in uwsgi.ini
uwsgi_pass unix:///tmp/dmoj-site.sock;
include uwsgi_params;
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
}
location /static {
gzip_static on;
expires max;
#root /tmp/static/;
# Comment out root, and use the following if it doesn't end in /static.
alias /tmp/static/; # 配置主项目的静态文件地址
}
# Uncomment if you are using PDFs and want to serve it faster.
# This location name should be set to DMOJ_PDF_PROBLEM_INTERNAL.
#location /pdfcache {
# internal;
# root <path to pdf cache diretory, without the final /pdfcache>;
# # Default from docs:
# #root /home/dmoj-uwsgi/;
#}
# Uncomment if you are allowing user data downloads and want to serve it faster.
# This location name should be set to DMOJ_USER_DATA_INTERNAL.
#location /datacache {
# internal;
# root <path to data cache diretory, without the final /datacache>;
#
# # Default from docs:
# #root /home/dmoj-uwsgi/;
#}
# Uncomment these sections if you are using the event server.
location /event/ {
proxy_pass http://127.0.0.1:15100/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
location /channels/ {
proxy_read_timeout 120;
proxy_pass http://127.0.0.1:15102;
}
}
# 测试nginx配置文件并使之生效
sudo nginx -t
sudo nginx -s reload
- 配置 event
新建文件 site/websocket/config.js,内容如下。并根据该文件修改 nginx 的配置文件中 event
和 channels
对应端口。同时修改 local_setting.py 文件中 EVENT_DAEMON_POST
、EVENT_DAEMON_GET
和 EVENT_DAEMON_POLL
三个变量值。
module.exports = {
get_host: '127.0.0.1',
get_port: 15100,
post_host: '127.0.0.1',
post_port: 15101,
http_host: '127.0.0.1',
http_port: 15102,
long_poll_timeout: 29000,
};
# 安装ws依赖
npm install qu ws simplesets
pip3 install websocket-client
# 重启程序使修改的配置生效
sudo supervisorctl update
sudo supervisorctl restart bridged
sudo supervisorctl restart site
sudo nginx -s reload
安装判题服务器
- 安装判题后端
sudo apt install python3-dev python3-pip build-essential libseccomp-dev -y
pip3 install dmoj
- 判断系统支持判题语言
dmoj-autoconf # 执行后会打印支持的runtime
# 将刚才打印的runtime复制到新建文件judge.yml的runtime部分
# 此处的id和key配置与后端管理界面中判题服务器设置一直即可
id: <judge name>
key: <judge authentication key>
problem_storage_root:
- /home/ubunut/problem
runtime:
...
- 配置 supervisord
# judge.conf
[program:judge]
command=/home/ubuntu/dmojsite/bin/dmoj -c judge.yml localhost
directory=/home/ubuntu/site/
stopsignal=QUIT
stdout_logfile=/tmp/site.stdout.log
stderr_logfile=/tmp/site.stderr.log
使用 sudo supervisord update
更新配置并生效,在后端管理界面发现判题服务器在线状态为在线。
验证和总结
到此,DMOJ 就安装完成了。从整个安装过程来看,一个 OJ 系统所包含的组件的确挺多,各种各样的配置来回修改,很容易让人一不留神就落下某个配置而使得程序无法正常运行。当然也有在测试过程中是可以正常运行的,而使用 supervisord 和 nginx 之后突然就不能了。我在实践过程中,对于官方提供的不同程序使用用户可能造成的文件权限不足,从而导致程序无法运行的问题感受颇深。折腾了很久也没有找出其中哪个步骤少了或者文件路径不对,后来发现主项目目录没有权限访问执行会导致一系列的问题。如果对用户管理和文件权限安全管理非常了解的话,还是按照官方的做法来设置,毕竟这样能保证操作系统的安全。
版权声明: 如无特别声明,本文版权归 仲儿的自留地 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 用 DMOJ 搭建属于自己的 OJ 》
本文链接:https://lisz.me/tech/linux/dmoj.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!