<?php
/**
 * 启动并监控cron的队列脚本状态
 *
 * @copyright (c) 2010, weibo All rights reserved.
 * @author 王勇 <wangyong1@staff.sina.com.cn>
 * @version 2.0 - 2011-11-25
 * @package Daemon
 */
date_default_timezone_set("Asia/Shanghai");
abstract class cronBase {
    protected $process;                     // 队列进程列表,必选项
    protected $procnumlist;                 // 队列进程数限制,必选项
    protected $procnum = 1;                 // 默认启动进程数
    protected $maxtimelist;                 // 队列进程最大执行时间限制列表
    protected $maxtime = 480;               // 队列一次循环的默认最长时间, 未自定义则该项有效, 超过该值进程将被杀死
    protected $bin_php = '/usr/bin/php'; // php执行文件路径

    private $path_daemon;               // ROOT根目录
    private $proc_start;                // 启动队列的进程信息
    private $path_prochealth;           // 进程健康路径
    private $path_conf;                 // 配置文件路径

    public function __construct() {
        $this->path_conf = dirname(dirname(__FILE__))."/config/daemonconf.php";
        include_once($this->path_conf);

        $this->path_daemon = dirname(dirname(__FILE__)).'/deal/';
        $this->path_prochealth = dirname(dirname(__FILE__)).'/cache/proc/deal_%s.txt';

        $this->setPara();
        $this->prepare();
        $this->checkProc();
        $this->startProc();

        return true;
    }

    /* 子类必须实现,设置队列运行所须属性 */
    public abstract function setPara();

    private function prepare() {
        $dir_prochealth = dirname($this->path_prochealth);
        if(!is_dir($dir_prochealth)) system("mkdir -p $dir_prochealth");
        if(empty($this->process)) exit("No dealscript.\n");
    }

    private function checkProc() {
        // 检测
        foreach($this->process as $k => $proc) {
            $active_proc_codes = array();
            // 搜集存活的进程号、数
            $path_proc = $this->path_daemon.$proc;
            $chk_shell = "ps -ef|grep '{$path_proc}'|grep -v grep|grep -v '\/bin\/sh'|awk -F ' ' '{print $2\" \"$10\" \"$11}'";

            $maxtime = $this->maxtimelist[$k]+0>0 ? $this->maxtimelist[$k] : $this->maxtime;

            $zombieProcNum = 0;
            $fp = @popen($chk_shell, 'r');
            while (!feof($fp)) {
                $buffer = fgets($fp, 4096);
                $procinfo = explode(' ', trim($buffer));
                $procid = $procinfo[0]+0;
                $proc_code = is_numeric($procinfo[2]) ?  $procinfo[2] : $procinfo[1]+0;

                if($procid==0) continue;

                // 杀死僵死进程
                $file_prochealth = sprintf($this->path_prochealth, $proc_code.'_'.md5($path_proc));
                if(file_exists($file_prochealth)) {
                    $lasttime_data = explode('|', file_get_contents($file_prochealth));
                    $lasttime = strtotime($lasttime_data[0]);
                    $nowtime = time();
                }

                if(!file_exists($file_prochealth) || ($nowtime-$lasttime > $maxtime)) {
                    system("/bin/kill -9 $procid");
                    error_log('['.date('Y-m-d H:i:s').']kill:'.$path_proc.'|'.$proc_code.'_'.md5($path_proc).'|'.$nowtime.'|'.$lasttime.'|'.$maxtime."\n", 3, dirname(dirname(__FILE__)).'/cache/proc/cron_track.log');
                    $zombieProcNum++;
                } else {
                    // 存活的进程
                    $active_proc_codes[] = $proc_code;
                }
            }

            $procnum = $this->procnumlist[$k]+0>0 ? $this->procnumlist[$k] : $this->procnum;
            for($i=0;$i<$procnum;$i++) {
                if(in_array($i, $active_proc_codes)) continue;
                $this->proc_start[$k]['proc'] = $path_proc;
                $this->proc_start[$k]['startnum'][] = $i;
            }

            if($fp) @pclose($fp);
        }
    }

    /* 检查进程运行情况检测, 杀僵死进程, 启动进程 */
    private function startProc() {
        // 启动队列
        if(empty($this->proc_start)) return ;
        foreach($this->proc_start as $k => $proc) {
            if(empty($proc['startnum'])) continue;

            foreach ($proc['startnum'] as $proc_code) {
                $cmd = "{$this->bin_php} {$proc['proc']} {$proc_code} &";
                //echo $cmd."\n";
                //error_log('['.date('Y-m-d H:i:s').']startproc:'.$cmd."\n", 3, dirname(__FILE__).'/cron_track.log');
                $fp = @popen($cmd, "r");
                if($fp) @pclose($fp);
            }
        }
    }

}