前期准备
选手机
由于ICPC World Final官方镜像2025的直接是磁盘镜像文件了,因此这回还是采用2024版本的镜像。
注意由于选手机需要完全断电再搬运,有的时间会乱掉,因此需要搭建起来一台NTP授时服务器,采用ntpd-rs
来搭建。
Natsume的static目录下的结构记录如下
|
|
其他部分依然维持之前的配置不变,批量化处理等现有的Natsume功能已经能够完全覆盖了,无须新的更改。
服务器
PHP相关配置
- 在
/etc/php/8.3/fpm/php.ini中设置memory_limit = 2G,因为题目被处理过程中如果样例点等过大会导致爆内存 - 修改MariaDB配置
/etc/mysql/my.cnf1 2 3 4[mysqld] max_connections=1000 innodb_log_file_size=512M max_allowed_packet=500M - 将
admin和jury加到DomJudge这个Team里面,这样上传题目的时候会自动测试Jury Solution - 上传完队伍图片和学校LOGO后要确认权限,
sudo chown -R www-data:www-data /opt/domjudge/domserver/webapp/public - CDS必须要使用Linux来跑,Windows上跑不起来会有奇奇怪怪的问题
- presAdmin和presClient注意要有不同的指令
1 2.\presAdmin.bat https://10.12.13.20:2335/api/contests/ presAdmin <password> .\client.bat https://10.12.13.20:2335/api/ presentation <password> - 注意CDS配置的时候CCS URL最后不能有/,不然的话他会把/也识别为contest id的一部分,很无语
- presClient的中文,这是老问题了需要换字体来修复
优化后的外榜反代
优化了外榜缓存,使其缓存所有需要的文件类型
|
|
打印
新编写的打印代码,主要是为了解决中文编码问题,直接换Typst了也省着Latex那么重的依赖了
比赛到一半有个队报告说他们无法打印,查了下才发现他们队名里面带# ""导致Typst编译不了了,紧急临时加了个转义给干过去了
|
|
对应的DomJudge内的打印指令如下
|
|
滚榜
Resolver的字体还需要更多字体,相比于presentation,直接替换成黑体会出问题,还得合并原始的和新的字体
在付出了接近3天的各种折腾时间后,鉴于实际滚榜的时候给了10GB内存仍然爆炸,我们一致同意写Resolver这玩意的人有点问题,与其折腾他不如自己重新写一个来得快,就跟Natsume一样,准备下回之前搓一个出来
赛中问题
相比于上回邀请赛的一团乱麻这回算是比预料中的好太多了,毕竟全程基本只有我一个人在搞,再加上OrangeJ师兄抽空帮我配一下评测机+赛时帮忙,之前搞过的部分这回基本没出啥问题
除了DomJudge在热身赛的时候莫名其妙的崩了,表现为CPU负载不高,PHP FPM进程看起来完全健康,然而就是直接访问不了,重启下PHP FPM立刻就能好,主要是这没有任何错误日志, 也无法稳定复现,因为前一天我们自己拿Oha试了下跑压力测试啥事没有,就很奇怪,后来准备把能想到的部分都改一遍,根据正赛情况判断主要问题应该出现在下面这个地方
|
|
默认的max_children是40,完全撑不起来赛场所有选手的访问,在这之后由于无法确认到底真的修复了没有,觉得起一种简单粗暴的办法,直接监控死了就重新拉,让GPT搓了一个出来
|
|
但即使这样,在正赛的最后,由于所有人全都再狂交直接把服务器CPU全部打满,到了是崩了下,但由于拉的够快选手应该几乎无感觉
全程监控可见主服务器宽带基本稳定在50MB/s的状态,而外榜服务器则全程打满,下回需要给两台服务器都搞成链路聚合的,不然网络压力过大了。
热身赛完事后,俩人全都一门心思在保证正赛别崩上,俩人谁都没搞过滚榜,也都没看出来Unfreeze Time不对🤦♂️,导致一结束直接放榜了,下回注意直接不设置Unfreeze Time就好了, 滚榜的话就看之后重新写一个解析程序来搞吧,Resolver实在是太难用了,很难想象一个最新稳定版没有之前版本兼容性强的程序…….
后续优化
优化PHP Session储存
检查PHP FPM Slowlog,可见如下
|
|
可见PdoSessionHandler处有大量的Slowlog,进一步分析可见PHP session全部存在了MariaDB中,导致所有登录的选手一次请求至少有两次读取数据库操作,
进而大幅增加了数据库负载,由于Symfony可以直接换Redis作为Session Store,完全不理解为什么要拿数据库存会话,甚至拿文件存都要比数据库好得多。因此切换成Redis作为会话存储
|
|
然后修改webapp/config/services.yaml
|
|
修改webapp/config/packages/framework.yaml
|
|
上述修改完成后不会直接生效,需要在Symfony Console中清理缓存,注意版本会影响console位置
- 对于DomJudge 8.3.2执行
php webapp/bin/console cache:clear - 对于DomJudge 9.0.0执行
php bin/dj_console cache:clear
优化宽带尖峰
赛间监控可见服务器宽带有大量的尖峰,后续分析可见,一次榜单的请求就有大约9MB的数据被传输,服务器是千兆宽带9MB一次请求再加上并发是一个很恐怖的量了, 因此需要给Nginx配置一下Botli压缩。
先下载Nginx的源码并编译依赖
|
|
然后拉取Botli的源码
|
|
接下来为了优化速度可以修改.gitmodules内的子模块地址,改成镜像进而加速拉取。
|
|
然后返回到nginx的源码目录并执行
|
|
注意ngx_brotli为刚刚拉取的源码的路径。
接下来看一下/usr/lib/nginx/modules/这个路径有没有,没有的话新建,然后把编译好的objs/*.so复制过去
|
|
然后把模块加载进去,修改/etc/nginx/nginx.conf,在http块外面添加
|
|
接着修改Nginx的配置,在server块中添加如下部分
|
|
这样便可以直接把9MB的HTML页面压缩到1MB以下,大幅降低宽带压力。
后面又配上了Open Telemetry并重新Profile了一下,得出结论DomJudge写的实在是太烂,所有地方都会是瓶颈,优化得不偿失不如重写。
优化代码
可以拿Xdebug进行一下profile,如下图是profile后的public榜单页面耗时

可见一个getConfig竟然花费了30%以上的时间,而且其中的array_map竟然也花费了很长时间,去看代码可见如下
|
|
无语好吧,这每次都要重新构建一次对象,那开销能不大么。怪不得压力测试的时候如下所示

直接让其在生成缓存的时候就是对应的对象,修改如下
|
|
再重新用k6进行压力测试,如下所示

可见所有指标均显著下降,重新profile如下图所示

assetPath那个问题已经有一个PR在修了,在此不多赘述。