昨天我发现禅道不能登录了,检查了一下发现禅道所在的目录没有空间了,排查了一下发现是禅道的定时任务每天备份数据库文件还有上传的附件,由于附件很大所以每天都占用大量的存储空间。
然后我删掉了那些多余的备份,另外想禁用掉禅道的自动备份。然后问题来了
禅道的定时任务是如何执行的呢?
首先想到的是crontab,因为禅道官网提供了一些脚本用来做这些操作的,它就生成了一些sh脚本,需要添加到cron里面,然后就可以定时执行了
问题是我检查了所有用的crontab,发现并没有禅道的任何定时任务项目。
这引起了我的好奇心,我决定追入禅道的代码看看究竟是什么原因使得禅道有了自动定时任务的功能
/** *Ajaxexeccron. * *@accesspublic *@returnvoid */ publicfunctionajaxExec() { ignore_user_abort(true); set_time_limit(0); session_write_close(); /*Checkcronturnon.*/ if(empty($this->config->global->cron))die(); /*makecronstatustorunning.*/ $configID=$this->cron->getConfigID(); $configID=$this->cron->markCronStatus('running',$configID); /*Getandparsecrons.*/ $crons=$this->cron->getCrons('nostop'); $parsedCrons=$this->cron->parseCron($crons); /*Updatelasttime.*/ $this->cron->changeStatus(key($parsedCrons),'normal',true); $this->loadModel('common'); $startedTime=time(); while(true) { /*Whencronisnullthendie.*/ if(empty($crons))break; if(empty($parsedCrons))break; if(!$this->cron->getTurnon())break; /*Runcrons.*/ $now=newdatetime('now'); $this->common->loadConfigFromDB(); foreach($parsedCronsas$id=>$cron) { $cronInfo=$this->cron->getById($id); /*Skipemptyandstopcron.*/ if(empty($cronInfo)or$cronInfo->status=='stop')continue; /*Skipcronthatstatusisrunningandruntimeislessthanmax.*/ if($cronInfo->status=='running'and(time()-strtotime($cronInfo->lastTime))<$this->config->cron->maxRunTime)continue; /*Skipcronthatlasttimeismorethanthiscrontime.*/ if($cronInfo->lastTime>$cron['time']->format(DT_DATETIME1))die(); if($now>$cron['time']) { $this->cron->changeStatus($id,'running'); $parsedCrons[$id]['time']=$cron['cron']->getNextRunDate(); /*Executioncommand.*/ $output=''; $return=''; if($cron['command']) { if(isset($crons[$id])and$crons[$id]->type=='zentao') { parse_str($cron['command'],$params); if(isset($params['moduleName'])andisset($params['methodName'])) { $this->app->loadConfig($params['moduleName']); $output=$this->fetch($params['moduleName'],$params['methodName']); } } elseif(isset($crons[$id])and$crons[$id]->type=='system') { exec($cron['command'],$output,$return); if($output)$output=join("\n",$output); } /*Savelog.*/ $log=''; $time=$now->format('G:i:s'); $log="$timetask".$id."executed,\ncommand:$cron[command].\nreturn:$return.\noutput:$output\n"; $this->cron->logCron($log); unset($log); } /*Revertcronstatus.*/ $this->cron->changeStatus($id,'normal'); } } /*Checkwhetherthetaskchange.*/ $newCrons=$this->cron->getCrons('nostop'); $changed=$this->cron->checkChange(); if(count($newCrons)!=count($crons)or$changed) { $crons=$newCrons; $parsedCrons=$this->cron->parseCron($newCrons); } /*Sleepsomeseconds.*/ $sleepTime=60-((time()-$now->getTimestamp())%60);//注意这里,sleep睡觉小于60秒 sleep($sleepTime); /*Breakwhile.*/ if(connection_status()!=CONNECTION_NORMAL)break; if(((time()-$startedTime)/3600/24)>=$this->config->cron->maxRunDays)break; } /*Revertcronstatustostop.*/ $this->cron->markCronStatus('stop',$configID); } |
这段代码是被footer.html.php文件里面的代码执行的
这段js代码是被浏览器执行的,但是因为已经有一个死循环在服务端执行了,如果这段代码被多次执行,完全是浪费服务器资源,禅道是如何避免多次执行的呢
if($this->loadModel('cron')->runable())js::execute('startCron()');
还是刚才footer文件里面的代码,意思是如果cron模块是可运行的,才执行那个ajaxcron代码,继续看runable的代码
publicfunctionrunable() { if(empty($this->config->global->cron))returnfalse; $lastTime=$this->getLastTime(); if($lastTime=='0000-00-0000:00:00'or((time()-strtotime($lastTime))>$this->config->cron->maxRunTime))returntrue; if(!isset($this->config->cron->run->status))returntrue; if($this->config->cron->run->status=='stop')returntrue; returnfalse; } |
$lastTime是cron表里面lastTime最新时间的值(没错,这个表里面的时间一直被上面所说的循环update),上面的代码是如果cron表里面最新的时间离现在已经超过<prename="code"class="php">maxRunTime(配置文件里面写的,是65秒),如果已经超过65秒了,那么认为是服务端那个死循环的php执行已经被停止了,就会自动在发起一个ajaxcron的请求。
禅道就是这么避免这个死循环被多次在执行的。也是通过这些代码来使得即使你没有配置系统的crontab,依然可以执行定时任务,这是个不错的思路。
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。