| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 | <?phpnamespace KIF\Cli;use KIF\Verify;use Exception;use KIF\Core\Config;/** * 这是一个辅助性的东西,当你希望某个操作只运行一个进程的时候(例如生成缓存,既然生成缓存,几十个同时跑,我觉得还不如不要呢) * 还有像优享团结团后,发成功通知的短信,发了一次还发?sb了呢! * * @author gaoxiaogang@gmail.com * */class SingleProcess {    /**     * 唯一标识     * @var string     */    private $pid;    /**     * 最大生存时间 单位:秒     * @var int     */    private $timeout;    /**     * 是否检查操作系统进程     * 该值为true时,将检查操作系统中的进程是否存在。如果不存在,返回已超时。     * 只用于(unix/linux)     * @var boolean     */    private $checkSystemProcess;    public function __construct($pid, $timeout = 600, $checkSystemProcess = false) {        if (!self::isValidPid($pid)) {            throw new Exception('invalid $pid');        }        if (!Verify::int($timeout) || $timeout < 1) {            throw new Exception('invalid $lifetime');        }        if (!is_bool($checkSystemProcess)) {            throw new Exception('invalid $checkSystemProcess');        }        $this->pid = $pid;        $this->timeout = (int) $timeout;        $this->checkSystemProcess = $checkSystemProcess;    }    /**     * 1、检测一个进程是否正在运行,如果返回true 那么就是正在运行,如果false 表示已经结束,你可以放心使用!     * 2、当进程已经超时,也会返回false     * 3、如果指定$this->checkSystemProcess为true,将检查操作系统中的进程是否存在。如果不存在,返回false。     *     * @return boolean     */    public function isRun() {        $pf = $this->getPidPath();        # 不存在        if (!file_exists( $pf )) {            return false;        }        # 超时了        if ($this->isTimeout()) {            $this->kill();            return false;        }        # 系统进程不存在了,就认为前一个进程已经结束了。        if ($this->checkSystemProcess) {	        $spid = $this->getSystemPid();	        if (!self::existSystemProcess($spid)) {	        	return false;	        }        }        return true;    }    /**     * 开始运行一个进程(声明:这只是一个虚拟的辅助操作,不要乱想)     *     * @return Boolean     */    public function run() {        file_put_contents($this->getPidPath(), getmypid());        return true;    }	/**	 * 激活	 * 如果new SignleProcess 时指定的timeout是600秒,当这个时间到达时,另一个进程就会将当前进程杀死。	 * 而active方法的作用,就是重新计算超时时间。	 * @param $activeFrequency 激活频率   300秒,即五分钟激活一次	 * @return boolean	 */	public function active($activeFrequency = 300) {		static $time_prev = null;		if (is_null($time_prev)) {			$time_prev = time();		}		$time_now = time();	    # 达到激活频率,声明脚本还活着	    if ($time_now - $time_prev >= $activeFrequency) {	        $time_prev = $time_now;//更新上次激活时间	        $this->run();	        return true;	    }	    return false;	}    /**     * 进程结束     *     * @return Boolean     */    public function complete() {        @unlink($this->getPidPath());        return true;    }    /**     * 终止一个进程     *     * @return Boolean     */    public function kill() {    	$pf = $this->getPidPath();        if (!file_exists($pf)) {            return true;        }        if ($this->checkSystemProcess) {        	$spid = $this->getSystemPid();        	if ($spid) {        		exec("kill -9 {$spid}");        	}        }        @unlink($pf);        sleep(1);        return true;    }    /**     * 清除所有进程记录,不管有没结束     *     * @param boolean $checkSystemProcess 是否检查操作系统进程     * @return Boolean     */    static public function cleanup($checkSystemProcess = false) {        $path = self::getPath().'*';        foreach (glob($path) as $filename) {        	if ($checkSystemProcess) {        		$spid = file_get_contents($filename);        		if (self::existSystemProcess($spid)) {        			exec("kill -9 {$spid}");//同时杀死该pid对应的操作系统进程        		}        	}            @unlink($filename);        }        sleep(10);//休眠10秒        return true;    }    /**     * 取得缓存路径,如果不存在,自动创建     *     * @return String     */    static private function getPath() {    	# TODO 配置文件中 Log_Path    	$logPath = Config::getInstance ()->get ( 'Log_Path' );    	    	if(empty($logPath)){    		throw new Exception('Config  Log_Path is empty');    	}    	        $path  = $logPath . DS . 'sspfiles' . DS;        if (!file_exists($path)) {            if (!mkdir($path, 0777, true)) {                return false;            }        }        return $path;    }    /**     * 是否超时     * @return Boolean     */    private function isTimeout() {        if ((time() - filemtime($this->getPidPath())) > $this->timeout) {            return true;        }        return false;    }    /**     * 获取存放pid的全路径,包括pid文件名     * @return string     */    private function getPidPath() {        return self::getPath() . md5($this->pid);    }    /**     * 获取系统进程pid     * @return int | false     */    private function getSystemPid() {        $spid = @file_get_contents($this->getPidPath());        if ($spid) {            return $spid;        }        return false;    }    /**     * 是否存在操作系统进程     *     * @param int $spid 操作系统进程id     * @return Boolean     */    static private function existSystemProcess($spid) {    	if (!Verify::int($spid) || $spid < 1) {    		return false;    	}        $cmd = 'ps -eo pid | egrep ^\ *'.$spid.'$';        $out = shell_exec($cmd);        return empty($out) ? false : true;    }    /**     * 是否有效的pid     * @param string $pid     * @return boolean     */    static private function isValidPid($pid) {        if (!is_string($pid) || empty($pid)) {            return false;        }        return true;    }}
 |