最近发现论坛内有些用户当月在线时间被清空,究其原因发现是清空当月在线时间的计划任务在某一错误时刻被执行。
就本帖分析下计划任务的实现过程,方便用户排查错误。
数据库结构:
论坛内现有的计划任务数据被保存在pre_common_cron表中,表中数据与论坛后台计划任务列表中的数据一致。
weekday字段为X表示每周星期X执行计划任务,day字段为X表示每月X日执行计划任务。X为-1表示不限制,即每天都执行计划任务。
执行计划任务:
计划任务执行函数discuz_cron::run()
注意:
每一个入口文件,如forum.php,space.php都有计划任务执行的入口,但打开一次页面只执行一条计划任务。
总结:
计划任务涉及的文件并不多,如果计划任务出现异常,通常只需要将class_core.php,class_cron.php重新上传覆盖即可。
就本帖分析下计划任务的实现过程,方便用户排查错误。
数据库结构:
论坛内现有的计划任务数据被保存在pre_common_cron表中,表中数据与论坛后台计划任务列表中的数据一致。
weekday字段为X表示每周星期X执行计划任务,day字段为X表示每月X日执行计划任务。X为-1表示不限制,即每天都执行计划任务。
执行计划任务:
执行计划任务在class_core.php中,初始化计划任务的函数_init_cron()中
function _init_cron() {
$ext = empty($this->config['remote']['on']) || empty($this->config['remote']['cron']) || APPTYPEID == 200;
if($this->init_cron && $this->init_setting && $ext) {
if($this->var['cache']['cronnextrun'] <= TIMESTAMP) {//判断当前是否有计划任务出于待执行状态
require_once libfile('class/cron');
discuz_cron::run();//执行计划任务
}
}
}
计划任务执行函数discuz_cron::run()
function run($cronid = 0) {
global $_G;
$timestamp = TIMESTAMP;
$cron = DB::fetch_first("SELECT * FROM ".DB::table('common_cron')."
WHERE ".($cronid ? "cronid='$cronid'" : "available>'0' AND nextrun<='$timestamp'")."
ORDER BY nextrun LIMIT 1"); //取出一条符合执行条件的计划任务
$processname ='DZ_CRON_'.(empty($cron) ? 'CHECKER' : $cron['cronid']);
if($cronid && !empty($cron)) { //为了手动执行计划任务解锁
discuz_process::unlock($processname);
}
if(discuz_process::islocked($processname, 600)) { //检查计划任务进程是否上锁
return false;
}
if($cron) { //计划任务执行部分
$cron['filename'] = str_replace(array('..', '/', '\\'), '', $cron['filename']);
$cronfile = DISCUZ_ROOT.'./source/include/cron/'.$cron['filename'];
$cron['minute'] = explode("\t", $cron['minute']);
discuz_cron::setnextime($cron); //根据后台设置,更新该计划任务执行的时间
@set_time_limit(1000);
@ignore_user_abort(TRUE); //设置与客户机断开不会终止脚本的执行
if(!@include $cronfile) { //执行具体计划任务程序
return false;
}
}
discuz_cron::nextcron(); //设置最近一次计划任务执行的时间
discuz_process::unlock($processname); //解锁进程
return true;
}
注意:
每一个入口文件,如forum.php,space.php都有计划任务执行的入口,但打开一次页面只执行一条计划任务。
总结:
计划任务涉及的文件并不多,如果计划任务出现异常,通常只需要将class_core.php,class_cron.php重新上传覆盖即可。