就本帖分析下計劃任務的實現過程,方便用户排查錯誤。
數據庫結構:
論壇內現有的計劃任務數據被保存在 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(" ", $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 重新上傳覆蓋即可。