Page 1 of 3123

Vakuum开发笔记01 开天辟地

設計開發 5 Comments »139 views

1.缘起

先驱——COGS

早在2008年,我自学PHP后开发了COGS,并成功用于学校内部的OJ,ruvtex。也曾经对外开放过,但是由于学校网络不稳定,后来一直连不上了。我还把COGS推荐给了OOJ,只是直到现在都过于冷清。随着COGS功能不断完善,体系越来越庞大,Bug也非常多。限于当时水平,架构非常混乱,以至于到无法继续维护的地步,于是我遗憾的宣布了COGS的死亡。随后我又萌生了一个重新设计的念头,只是高二时期忙于NOI和文化课的学习,这一计划就一直被搁置到NOI2009结束。

知识准备

NOI2009结束以后,我终于有了时间,可以进行我新的OJ的开发了。之前的COGS不敢开源,因为写的太烂,开源的话肯定会黑得万劫不复。不过新的OJ不同,为了提高对自己要求,我决定对其开源。

我在暑假时期阅读了有关PHP高级开发、MVC架构、Javascript、CSS、Linux系统编程等大量书籍。受到经典的架构Zend Framework的启发,起初决定用它开发,但是学习不久就发现Zend Framework过于庞大,学习难度太大,并且难以找到比较好的资料。在Zend Framework的官方网站上阅读纯英文的文档实在太费力了,而且效果不好,始终没有弄明白其本质。开源界有一个箴言,叫做“不要重新发明轮子”。什么意思呢?就是说很多开发人员总是在做一些前人已经做过,而且做得很好的工作。比如libxml2已经是一个非常优秀的C语言XML库了,你再自己写一个XML解析库,就是在浪费时间,尽管也许你做的不错,但也最多算的上是“重新发明了轮子”。起初我非常笃信这句“箴言”,拼死也要学Zend Framework,做了大量的无用功。但后来细细一想,我们最初学算法、数据结构的时候,不是都是要自己实现每一个细节吗?尽管库函数可能已经做得很好了,如qsort,平衡树。而我现在也是MVC架构的初学者,在对其理解不是很深刻的情况下,直接学习某个库、某个架构,是非常不明智的行为。因此我决定要“发明轮子”——自己开发一个合适的架构,就叫——BYVoid Framework Library(BFL)。

恰好当时父亲的公司准备建一个产品介绍性网站,为节约成本就把开发的任务交给了我。我想这是一个难得的练手的机会,决定用我的BFL开发。果然在开发的时候遇到了前所未有的问题,但是都一个个迎刃而解了,而且获得了不少经验,其中包括Apache2 .htaccess的设置。两个星期以后,公司的网站制作完成,我的”MVC架构轻量级内容管理系统”也发布了。

2.Vakuum诞生之初与架构设计

命名

2009年9月,我正式开始开发新的OJ。在开发先我想先起个名字——就像“没有名字的船不会带来好运”。起初准备叫vacuum,英语意思是“真空”,算是Beyond the Void的衍生。但是我不幸地发现这个名字已经在sourceforge上被占用了,后来决定改名为vakuum,即vacuum的德语拼写方法。

基础结构

Vakuum作为一个OJ,我把它剖分为了三个部分,vakuum-web,vakuum-judge和judger。vakuum-web是一个网站,用于和用户交互,处理各种请求,它是一个PHP网站,应该能够跨平台vakuum-judge则是评测机终端,用于接收来自vakuum-web的评测任务,评测以后返回结果。而judger是评测的核心,其中包含编译器compiler、执行器executor和检查器checker三部分,分别用于编译用户程序、执行用户程序和比对用户输出与测试数据。简而言之,vakuum-web是一个用户与核心的中介,而vakuum-judge则是judger的通信接口。我的目标是网站和评测分离,并允许多评测机协同工作,因此vakuum-web和vakuum-judge之间少不了通信。

通信设计

参考很多成熟OJ的结构,发现几乎都是把vakuum-judge模块实现为一个常驻进程daemon,不断检查数据库是否有新的任务出现,对其评测,但后写回数据库。多数设计都没有分离vakuum-web和judger,即限定了只能有一个评测机,少数可以实现分离,但是实际上是多写了一个通信程序。我的想法是vakuum-judge也用PHP实现,这样就避免了自己写一个socket通信程序,而且不需要额外获得底层权限以常驻进程。这样只需要接收来自vakuum-web的HTTP请求,对其处理,然后将结果写回。可是这样的一个同步传输方案有很大的问题,即vakuum-web发送请求以后需要长时间被阻塞在通信上,等待vakuum-judge评测的结束。如此一来加大了传输的风险,二来加重vakuum-web的服务器负载,三来还无法让用户看到评测的进度。因此我想出了一个异步传输方案,即vakuum-web只发送请求,完毕后就断开连接,之后等待vakuum-judge的回传即可。然而PHP有一个特性,就是用户浏览器断开连接以后,PHP脚本也会停止执行,vakuum-web是在模拟用户浏览器发送请求,断开链接以后就相当于浏览器按下了“停止”键,vakuum-judge正在执行的脚本不管到了哪里都会停止。查资料以后才知道PHP有这样的一个函数,ignore_user_abort(),忽略用户中止,就是用来对付这种情况的。

队列处理

通信方式设计很成功了,还有一个问题就是如何处理评测队列。这是一个棘手的问题,要么为什么有那么多大型OJ经常卡在评测队列上,出现Waiting长龙呢?几乎所有的OJ都是写了一个常驻进程的daemon处理队列。我想为了实现多评测机调度,这个daemon必须是现在vakuum-web端,但是这样就违背了我当初vakuum-web能够“跨平台”的设想,而且vakuum-web必须获得系统底层权限——不仅维护不便,还有安全隐患。

绞尽脑汁以后,我想出了“链式反应”的想法,其实也是受了通信设计方式的启发。这种方法不需要获得底层权限,不用额外写一个daemon,能够跨平台,不用为安全性额外操心,还可以充分利用先前写好的代码,究竟是什么方法呢?其实就是用一个PHP进程来充当队列处理器,这个队列处理器不需要常驻内存,仅仅在用时才会出现。就是当用户提交一个评测任务以后,如果有空闲评测机的话,立即对任务进行处理,然后当vakuum-judge返回评测完毕的信号时,vakuum-web端再对评测任务队列进行检查,看看有没有新的任务出现,如果有的话,立刻执行即可。这种方法很简洁,而且支持多评测机协同工作,因为每次结果返回时都要检查队列,就好像链式反应,或者多米诺骨牌一样,只要处理了第一个,后面的就都会接着被处理。

这种队列处理方法的唯一缺陷在于依赖评测机的返回信号,如果评测机那边出了什么问题,或者因为通信原因vakuum-web没有正常接收到信号,队列就会被卡住。因此首先需要保证的是vakuum-judge需要绝对的安全和稳定,除非无可抗拒理由(如网线被拔),不会因为任何原因而不正常返回信号。此外还要增加人工干预手段(如强制继续处理队列),避免真的不可抗拒原因的到来。

——————————————————————————————————

先到此为止,欢迎继续关注Vakuum开发笔记,下次将要写的是评测机核心(judger)的设计。目前的开发进度停滞在后台管理各种繁杂的细枝末节的处理上,下图的是目前的后台管理界面截图。

点击看全图

标签:, , , , , , ,

开源的在线评测系统——Vakuum

設計開發 16 Comments »299 views

项目地址 http://code.google.com/p/vakuum-oj/

简介

Vakuum是一个基于Linux+PHP的在线评测系统(Online Judge),主要面向信息学竞赛(OI)和ACM/ICPC。

功能

在线评测

用户提交程序代码,在线编译,执行测试数据,对其评判并返回结果。支持自定义检查器(Speciel Judge)。支持特殊类型题目(代码检查、交互式、库式、提交答案式)。

比赛举办

兼顾OI和ACM,支持个人赛、团队赛。比赛模式有在线赛(ACM),离线赛(OI),自由计时赛(USACO)。

晋级模式

根据现有题库,由管理员设置或用户推荐一组晋级路线,即做完一组题目后才能进入下一组,并获得相应称号。参加USACO和UVA。

问题讨论

简洁、高效、可控。

其他功能

题库检索,用户排名,队伍系统,权限管理,代码备份,……

架构

Vakuum 系统分为vakuum-web和vakuum-judge两大模块。vakuum-web是一个在线评测系统界面,需要PHP和MySQL支持,可以建立 在Linux或Windows平台下。vakuum-judge是评测系统的核心模块,负责处理用户评测请求,与vakuum-web进行通信。通信模块 为PHP,核心由C语言编写,只能运行在Linux(Unix)平台下。

vakuum-web是基 于PHP和MySQL的一个B/S应用程序,采用MVC(模型-视图-控制器)架构设计,负责与用户交互和与核心通信。支持于评测机分离或和一,支持多评 测机协同作业,不需要daemon和底层权限。用户通过访问vakuum-web查看题目,提交代码,vakuum-web将请求派发给vakuum- judge终端。支持多评测机数据同步,支持测试数据分发管理。主要采用HTTP信道发送命令,FTP信道发送数据。

vakuum- judge通信模块由PHP编写,收到请求以后,使用C语言编写的核心对代码进行编译,并在执行器沙盒中执行,保证系统安全。执行器使用ptrace监控 用户程序的系统调用和文件调用,根据需求加以放行或禁止,同时支持内存峰值统计,内存限制,用户时间统计,时间限制,输出文件大小限制。

现状

正在开发,vakuum-judge核心部分基本完成,vakuum-web底层架构设计完成,正在完善功能。现需要对PHP熟悉的开发者,善于网页设计和美化的开发者,精通Linux系统编程的开发者,有意将内核移植到Windows的开发者,欢迎有意者加入开发

标签:, , , , , , , ,

MVC架构轻量级内容管理系统

PHP, 設計開發 No Comments »86 views

本系统系本人学习之作。借鉴ZendFramework,开发了一个Model-View-Controller架构(BYVlib),成功实现了视图、模型、控制器分离,松散耦合,方便维护。PHP脚本文件全部在HTTP公共目录之外,公共目录public内只保留了图像、样式表和脚本文件。该系统分为页面、新闻、产品三大模块,用户可以自定义任何页面,并定制访问别名。该系统使用Apache的 Rewrite美化了路径,避免了难看的?结构。

源代码根据BSD协议发布,欢迎在此下载

刚刚用本系统为老爸公司做了个网站,地址http://www.cdxinwei.com/。另外对本系统感兴趣的欢迎与我联系,希望我们可以相互交流经验,共同学习,进步。

标签:, , , ,

WordPress OOP 一拖多实现

PHP, 設計開發 10 Comments »164 views

现在在一个空间上只放一个WordPress已经无法满足我的需要,以前总是上传多个WordPress,分别维护管理。但是每当插件更新或者WP更新的时候,维护起来很麻烦。

看过CMB的http://www.cbmland.com/post/544/oop-use-wordpress-as-class.html后,我也进行了这方面的尝试,但是还会出现很多问题,例如永久链接。今天我终于解决了这些问题。

明确一点,WordPress把一个博客所有的配置信息都写入了数据库,文件上仅仅是程序和主题,这样,我们只需根据访问来源的不同,给别给与两个设置即可。“面向对象”形容得很恰当。

在这个空间上,我也有两个WordPress博客,域名分别是http://www.byvoid.com/blog和,http://pal.byvoid.com,两个博客共用/home/myself/www/blog下面的WordPress程序。我把wp-config.php修改一下

if($_SERVER["HTTP_HOST"]=="www.byvoid.com")
{
define('DB_NAME', 'user');
define('DB_USER', 'user');
define('DB_PASSWORD', 'password');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
$table_prefix = 'wp_';
define ('WPLANG', 'zh_CN');
}
else if ($_SERVER["HTTP_HOST"]=="pal.byvoid.com")
{
define('DB_NAME', 'user');
define('DB_USER', 'user');
define('DB_PASSWORD', 'password');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
$table_prefix = 'pal_wp_';
define ('WPLANG', 'zh_CN');
}
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');
?>

经过这样修改,根据来源的不同,WordPress会读取两套不同的数据库设置,从而实现OOP。通过这种方法,我们理论上可以1拖无限个博客。

但是仅仅这样还是会出现问题,Rewrite永久链接会出错。

为什么为出错呢?因为我们只对WordPress进行了OOP改造,但是.htaccess文件还是没有分别。例如我的博客,www.byvoid.com的根目录为/home/myself/www,而pal.byvoid.com的根目录为/home/myself/www/blog,可以理解为后者是前者的子集。

修改www.byvoid.com的永久链接时,WordPress会在/home/myself/www/blog文件夹向.htaccess文件自动写入

# BEGIN WordPress

RewriteEngine On
RewriteBase /blog/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]

# END WordPress

而修改pal.byvoid.com时,WordPress又会在.htaccess写入

# BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress

于是发生了冲突,两个博客的Rewrite永久链接总是会有一个无法使用。于是,我考虑,.htaccess时都可以像wp-config.php一样实现判断呢?看过mod_rewrite的用法,终于有了解决方案。

我们可以使用RewriteCond进行来源判断,只需把.htaccess改成以下内容即可

# BEGIN WordPress

RewriteEngine On

RewriteBase /blog/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{SERVER_NAME} ^www.byvoid.com
RewriteRule . /blog/index.php [L]

RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{SERVER_NAME} ^pal.byvoid.com
RewriteRule . /index.php [L]

# END WordPress

修改过后,两个博客的永久链接都可以使用了。这种方理论上也可以推广到多个博客。

注意,只有虚拟主机的设定中,当前目录选项有AllowOverride All时,.htaccess才能用。.htaccess如何在Windows下建立,不用我说了吧?cmd下rename一个其他文件为.htaccess就行了。关于.htaccess文件,可以看看介绍

BYVoid原创。

标签:, , , , , , ,

PHP 计算相对路径

PHP No Comments »398 views

在PHP编程中,经常会遇到相对路径的计算,然而标准库中没有提供相应的函数,我只好自己编写了一个函数用于计算相对路径。

<?
function pathconvert($cur,$absp)//当前文件,目标路径
{
	$cur = str_replace('\\','/',$cur);
	$absp = str_replace('\\','/',$absp);
	$sabsp=explode('/',$absp);
	$scur=explode('/',$cur);
	$la=count($sabsp)-1;
	$lc=count($scur)-1;
	$l=max($la,$lb);
 
	for ($i=0;$i<=$l;$i++)
	{
		if ($sabsp[$i]!=$scur[$i])
			break;
	}
	$k=$i-1;
	$path="";
	for ($i=1;$i<=($lc-$k-1);$i++)
		$path.="../";
	for ($i=$k+1;$i<=($la-1);$i++)
		$path.=$sabsp[$i]."/";
	$path.=$sabsp[$la];
	return $path;
}
 
$path=pathconvert("/home/web/test/a.php","/home/data/d.png");
// $path="../../data/d.png"
?>
标签:, , ,
25 queries. 0.634 seconds. Designed by NattyWP .
Images by desEXign.