MarkJane
4/12/2017 - 7:35 AM

PHP守护进程管理.php Forker-可以让-php-cli-进程借助-nohup-以守护进程的方式运行.php

PHP守护进程管理.php Forker-可以让-php-cli-进程借助-nohup-以守护进程的方式运行.php

<?php
 
// 运行实例,fork 10 个进程,每个进程输出一行 Im a worker ,并保存在 /tmp/forker.log 中:
// CLI命令: php Forker.php 10
/*
if (empty($argv[1])) {
    echo "Im a worker\n";
    sleep(10);
    exit();
} else {
    $forker = new Forker('/tmp/forker.log');
    $forker->fork($forker->findCommand('php') . ' ' . __FILE__, (int)$argv[1] <= 0 ? 10 : (int)$argv[1]);
}
*/
 
/**
 * Forker 可以让 php-cli 进程借助 nohup 以守护进程的方式运行。
 * 这个 Forker 仅仅是让进程成为守护进程,不会复制父进程的内存。
 */
class Forker {
     
    private $nohub = '/usr/bin/nohup';
    private $out   = '/tmp/forker.log';
     
    /**
     * @param string $output 输出文件的路径。进程的标准输出将重定向到此文件
     * @throws \RuntimeException
     */
    public function __construct($output = '') {
        if (false !== ($nohup = $this->findCommand('nohup'))) {
            $this->nohub = $nohup;
        }
        if (!is_executable($this->nohub)) {
            throw new \RuntimeException('nohup not excutable');
        }
        if ($output) {
            $this->setOutput($output);
        }
    }
     
    /**
     * 设置输出文件的路径。进程的标准输出将重定向到此文件
     * @param string $file
     * @return \Forker
     * @throws \RuntimeException
     */
    public function setOutput($file) {
        if (!is_file($file)) {
            $dir = dirname($file);
            if ((!is_dir($dir) && !mkdir($dir, 0755, true)) || !is_writable($dir)) {
                throw new \RuntimeException('output is not writable, can not create output');
            }
        } else if (!is_writable($file)) {
            throw new \RuntimeException('output is not writable');
        }
        $this->out = $file;
        return $this;
    }
     
    /**
     * 获取输出文件的路径
     * @return string
     */
    public function getOutput() {
        return $this->out;
    }
     
    /**
     * 执行命令
     * @param string $command 命令。命令中的文件参数需要使用绝对路径
     * @param int $forks fork的进程数
     */
    public function fork($command, $forks = 1) {
        for ($i = 0; $i < $forks; ++$i) {
            $this->execute($command);
        }
    }
     
    /**
     * 根据当前环境查找命令的绝对路径
     * @param string $name
     * @return boolean
     */
    public function findCommand($name) {
        $file = trim(exec("which {$name}"));
        if (is_file($file) && is_executable($file)) {
            return $file;
        }
        return false;
    }
     
    /**
     * 执行命令,成功返回 true,失败返回 false
     * @param string $command
     * @return boolean
     */
    private function execute($command) {
        $lines = [];
        $code  = 0;
        exec("{$this->nohub} {$command} >> {$this->out} 2>&1 &", $lines, $code);
        if (0 !== (int)$code) {
            file_put_contents($this->out, "fork {$command} FAILD[{$code}]:\n" . implode("\n", $lines) . "\n", FILE_APPEND);
            return false;
        }
        file_put_contents($this->out, "fork {$command} OK\n", FILE_APPEND);
        return true;
    }
     
}
<?php defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * +-----------------------------------
 * PHP守护进程管理
 * +-----------------------------------
 *
 */

class Php_daemon extends GOMS_Controller {
    
    public function __construct()
    {
        parent::__construct();
    }
    
    /**
     * 守护进程列表
     *
     */
    public function index()
    {
        // 载入Redis相关配置
        $redis = redis_connect();
        
        // 载入守护进程任务配置
        $this->load->config('goms_php_daemon');
        $php_daemon = $this->config->item('php_daemon');
        
        foreach ($php_daemon as $key => $row) {
            $php_daemon[$key]['on-off_value'] = $redis->get($row['on-off_key']);
            // 查找其进程ID
            $fp = popen("ps -ef|grep '{$php_daemon[$key]['process_check_tag']}'|grep -v grep|awk '{print $2}'", "r");
            $pid = fread($fp, 512);
            $php_daemon[$key]['pid'] = $pid;
            pclose($fp);
        }
        
        $redis->close();
        
        $view_param = array(
            'ctl'           => $this->uri->segment(2),
            'php_daemon'    => $php_daemon
        );
        $this->load->view('monitor/php_daemon/list', $view_param);
    }
    
    public function on_off_do()
    {
        $taskKey = $this->input->post('taskkey');
        $onOffKey = $this->input->post('onoffkey');
        $value = $this->input->post('value');
        
        if (empty($onOffKey) || empty($value))
        {
            echo 'error';
            exit;
        }
        
        // 载入守护进程任务配置
        $this->load->config('goms_php_daemon');
        $php_daemon = $this->config->item('php_daemon');
        
        $taskInfo = $php_daemon[$taskKey];
        
        // 载入Redis相关配置
        $redis = redis_connect();
        $result = $redis->set($onOffKey, $value);
        $redis->close();
        
        if ($result === TRUE)
        {
            if ($value == 'on')
            {
                if (isset($taskInfo['process_num']))
                {
                    $process_num = $taskInfo['process_num'];
                }
                else 
                {
                    $process_num = 1;
                }
                for ($i = 0; $i < $process_num; $i++)
                {
                    $fp = popen($taskInfo['command'], "r" );
                    pclose($fp);
                }
            }
            else
            {
                // 查找其进程ID
                $pid = $this->_getProcessId($taskInfo['process_check_tag']);
                
                while ( ! empty($pid))
                {
                    $pid = $this->_getProcessId($taskInfo['process_check_tag']);
                }
            }
            echo 'success';
        }
        else
        {
            echo 'error';
        }
    }
    
    /**
     * 根据执行命令获取其进程ID
     *
     */
    private function _getProcessId($processCheckTag)
    {
        $fp = popen("ps -ef|grep '{$processCheckTag}'|grep -v grep|awk '{print $2}'", "r");
        $pid = fread($fp, 512);
        pclose($fp);
    
        return $pid;
    }
}