程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> PHP實現程序單例運行

PHP實現程序單例運行

編輯:關於PHP編程

PHP實現程序單例運行


 

一、場景描述:

最近我們一塊業務,需要不斷的監聽一個目錄的變化,如果目錄中有文件,則啟動PHP腳本處理掉。最初的方案是使用crontab執行sh腳本,腳本大概如下:

SOK=`ps -ef |grep /www/sender.sh | grep -v grep|wc -l`
if [[ $SOK < 2 ]];then
        for f in `ls /www/queue`; do
                        php /www/logsender.php /www/queue/$f
        done


實際運行中出現了異常:ps -ef | grep xxx的方式,可能無法正確的判斷進程是否正在執行,if條件永遠都不會成立,使得PHP腳本永遠不會執行。經過考慮後,決定建立一個獨立於其他模塊的,能夠實現進程單例運行的類,解決這個問題。

二、方案設計

1、通過PID文件實現進程單例

2、程序啟動、退出自動創建、刪除PID文件,做到不需要業務代碼考慮PID文件刪除

3、盡量保證代碼獨立性,不影響業務代碼

三、原理

1、啟動創建PID文件

2、綁定程序退出、被殺死等信號量,用於刪除PID文件

3、添加析構函數,對象被銷毀時,刪除PID文件

四、遇到的問題

程序正常退出時,無法捕獲到信號量,不知道是不是信號量選錯了,ctrl+c等信號是正常的,如果可以解決捕獲程序正常退出時的信號量,則可以替代析構函數方案,更加穩定

五、代碼
 

single();
 *
 */
 
class DaemonSingle {
 
    //PID文件路徑
    private $pid_dir;
 
    //PID文件名稱
    private $filename;
 
    //PID文件完整路徑名稱
    private $pid_file;
 
    /**
     * 構造函數
     * @param $filename
     * @param string $pid_dir
     */
    public function __construct($filename, $pid_dir='/tmp/'){
        if(empty($filename)) throw new JetException('filename cannot be empty...');
        $this->filename = $filename;
        $this->pid_dir = $pid_dir;
        $this->pid_file = $this->pid_dir . DIRECTORY_SEPARATOR . substr(basename($this->filename), 0, -4) . '.pid';
    }
 
    /**
     * 單例模式啟動接口
     * @throws JetException
     */
    public function single(){
        $this->check_pcntl();
        if(file_exists($this->pid_file)) {
            throw new Exception('the process is already running...');
        }
        $this->create_pid_file();
    }
 
    /**
     * @throws JetException
     */
    private function create_pid_file()
    {
        if (!is_dir($this->pid_dir)) {
            mkdir($this->pid_dir);
        }
        $fp = fopen($this->pid_file, 'w');
        if(!$fp){
            throw new Exception('cannot create pid file...');
        }
        fwrite($fp, posix_getpid());
        fclose($fp);
        $this->pid_create = true;
    }
 
    /**
     * 環境檢查
     * @throws Exception
     */
    public function check_pcntl()
    {
        // Make sure PHP has support for pcntl
        if (!function_exists('pcntl_signal')) {
            $message = 'PHP does not appear to be compiled with the PCNTL extension.  This is neccesary for daemonization';
            throw new Exception($message);
        }
        //信號處理
        pcntl_signal(SIGTERM, array(&$this, signal_handler));
        pcntl_signal(SIGINT, array(&$this, signal_handler));
        pcntl_signal(SIGQUIT, array(&$this, signal_handler));
 
        // Enable PHP 5.3 garbage collection
        if (function_exists('gc_enable')) {
            gc_enable();
            $this->gc_enabled = gc_enabled();
        }
    }
 
    /**
     * 信號處理函數,程序異常退出時,安全刪除PID文件
     * @param $signal
     */
    public function signal_handler($signal)
    {
        switch ($signal) {
            case SIGINT :
            case SIGQUIT:
            case SIGTERM:{
                self::safe_quit();
                break;
            }
        }
    }
 
    /**
     * 安全退出,刪除PID文件
     */
    public function safe_quit()
    {
        if (file_exists($this->pid_file)) {
            $pid = intval(posix_getpid());
            $file_pid = intval(file_get_contents($this->pid_file));
            if($pid == $file_pid){
                unlink($this->pid_file);
            }
        }
        posix_kill(0, SIGKILL);
        exit(0);
    }
 
    /**
     * 析構函數,刪除PID文件
     */
    public function __destruct(){
            $this->safe_quit();
    }
}



 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved