用 PHP 實作的 Daemon 類別。可以在伺服器上實作佇列或脫離 crontab 的排程任務。
使用的時候,繼承於這個類,並重寫 _doTask 方法,透過 main 初始化執行。
- class Daemon {
-
- const DLOG_TO_CONSOLE = 1;
- const DLOG_NOTICE = 2 const DLOG_ERROR = 8;
- const DLOG_CRITICAL = 16;
-
- const DAPC_PATH = '/tmp/daemon_apc_keys';
-
- /**
- /**
- * 使用者 ID
- *
- * @var int
- */
- public $groupID = 65533; // 沒人
-
- /**
- * 組 ID
- *
- * @var 整數
- */
- public $requireSetIdentity = false;
-
- /**
- * 設定身分失敗時終止守護程式?
-
- * @var bool
- * @since 1.0.3
- */
- public $pidFileLocation = '/ tmp /daemon.pid';
-
- /**
- * PID 檔案路徑
- *
- * @var string
- * @since 1.0.1
- */
- public $processLocation = '';
-
- /**
- * processLocation
- * 進程資訊記錄目錄
- *
- * @var string
- */
- public $ processHeartLocation = '';
-
- /**
- * processHeartLocation
- * 進程心跳包檔案
- *
- * @var string
- */
- public $homePath = '/';
-
- /**
- * 主路徑
- *
- * @var string
- * @since 1.0
- */
- protected $_pid = 0;
-
- /**
- * 目前進程 ID
- *
- * @var int
- * @since 1.0
- */
- protected $_isChildren = false;
-
- /**
- * 這個行程是子行程嗎
- *
- * @var boolean
- * @since 1.0
- */
- protected $_isRunning = false;
-
- /**
- * 守護程式正在執行
- *
- * @var boolean
- * @since 1.0
- */
- public function __construct() {
-
- error_reporting(0);
- set_time_limit(0);
- ob_implic); >
- register_shutdown_function(array(&$this, 'releaseDaemon'));
- }
-
- /**
- * 建構子
- *
- * @return void
- */
- public function main() {
- $this->_logMessage('正在啟動守護程序');
-
- if (!$this->_daemonize()) {
- $this->_logMessage('啟動啟動守護程序', self ::DLOG_ERROR);
-
- 回傳false;
- }
-
- $this->_logMessage('正在運作...');
-
- $this-> _isRunning = true;
-
- while ($this->_isRunning) {
- $this->_doTask();
- }
-
- 回傳true;
- }
-
- /**
- * 啟動進程
- *
- * @return bool
- */
- public function stop() {
-
- $this->_logMessage('守護停止程序');
-
- $this- >_isRunning = false;
- }
-
- /**
- * 停止進程
- *
- * @return void
- */
- protected function _doTask() {
- // 覆寫此方法
- }
-
- /**
- * 執行任務
- *
- * @return void
- */
- protected function _logMessage($msg, $level = self::DLOG_NOTICE) {
- // 覆寫此方法
- }
-
- /**
- * _logMessage
- * 記錄日誌
- *
- * @param string 訊息
- * @param integer 等級
- * @return void
- * @param integer 等級
- * @return void
- */
- private function _daemonize() {
-
- ob_end_flush();
-
- if ($this->_isDaemonRunning()) {
- // 守護程式已經執行。 return false;
- }
-
- if (!$this->_fork()) {
- // 無法分叉。 🎜> if (!$this->_setIdentity() && $this->requireSetIdentity) {
- // 必要的身份設定失敗。 !posix_setsid()) {
- $this->_logMessage('無法使當前進程成為會話領導者', self:: DLOG_ERROR);
-
- return false;
- }
-
- return false;
- }
-
-
- if (!$fp = fopen($this->pidFileLocation, 'w')) {
- $this-> ;_logMessage('無法寫入PID 檔案', self::DLOG_ERROR);
- return false ;
- } else {
- fputs($fp, $this->_pid);
- fclose ($fp);
- }
-
- // 寫入監控日誌
- $this->writeProcess();
-
- chdir($this->homePath);
- umask(0);
-
- 宣告(ticks = 1);
-
- pcntl_signal(SIGCHLD, array(&$this, 'sigHandler'));
- pcntl_signal(SIGTERM, array(&$this, 'sigHandler' cntl_signal(SIGTERM, array(&$this, 'sigHandler') cntl_signal(SIGTERM, array(&$this, 'sigHandler') cntl_signal;
- pcntl_signal(SIGUSR1, array(&$this, 'sigHandler'));
- pcntl_signal(SIGUSR2, array(&$this, 'sigHandler'));
-
- return true; }
-
- /**
- * Cheks 守護程式已在執行
- *
- * @return bool
- */
- 私有函數_isDaemonRunning() {
-
- $oldPid = file_get_contents($this->pidFileLocation);
-
- ($oldPid !== false && posix_kill(trim($oldPid),0))
- {
- $this->_logMessage('守護程式已經運行,PID: '.$oldPid, (self: :DLOG_TO_CONSOLE | self::DLOG_ERROR));
-
- 回傳true;
- }
- else
- {
- 回傳false;
- }
- }
- 回傳false;
- }
- }
- * 分岔進程
- *
- * @return bool
- */
- private function _fork() {
-
- $this->_logMessage('分叉...');
-
- $pid = pcntl_fork( );
-
- if ($pid == -1) {
- // 錯誤
- $this->_logMessage('無法分叉', self::DLOG_ERROR);
-
- return false;
- } elseif ($pid) {
- // 父程序
- $this->_logMessage('正在殺死父程序');
-
- exit();
- } else {
- // fork 的子程序
- $this->_isChildren = true;
- $this->_pid = posix_getpid();
-
- return true;
- }
- }
-
- /**
- * 設定守護程式的身份並回傳結果
- *
- * @return bool
- */
- private function _setIdentity() {
-
- if (!posix_setgid($this->groupID) || !posix_setuid($thisix_setuid($this->groupID) || !pos ->userID))
- {
- $this->_logMessage('無法設定身分', self::DLOG_WARNING);
-
- return false;
- }
- else
- return false;
- }
- else
- {
- return true;
- }
- }
-
- /**
- * 訊號處理程序
- *
- * @access public
- * @since 1.0
- * @return void
- */
- public function sigHandler($sigNo) {
-
- Noswitch ($sig )
- {
- case SIGTERM: // 關機
- $this->_logMessage('關機訊號');
- exit();
- break;
-
- case SIGCHLD: case SIGCHLD: // Halt
- $this->_logMessage('Halt signal');
- while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
- break;
- case SIGUSR1: //用戶定義
- $this->_logMessage('用戶定義信號1');
- $this->_sigHandlerUser1();
- break;
- case SIGUSR2: // 用戶定義
- $this ->_logMessage('使用者定義訊號2');
- $this->_sigHandlerUser2();
- break;
- }
- }
-
- /**
- * Signals handler: USR1
- * 主要用於定時清理每個行程裡被快取的網域名稱dns解析記錄
- *
- * @return void
- */
- 受保護函數_sigHandlerUser1() {
- apc_clear_cache('user');
- }
-
- /* *
- * Signals handler: USR2
- * 用於寫入心跳包檔案
- *
- * @return void
- */
- protected function _s) >
- $this->_initProcessLocation();
-
- file_put_contents($this->processHeartLocation, time());
-
- return true;
- }
- }
- /**
- * 釋放守護程式 pid 檔案
- * 退出時呼叫此方法(類似析構函式)
- *
- * @return void
- */
- public function releaseDaemon() {
-
- if ($this->_isChildren && is_file($this->pidFileLocation)) {
- $this-> '釋放守護程式');
- unlink($this->pidFileLocation); } }
-
- /**
- * writeProcess
- * 將目前進程資訊寫入監控日誌,另外的腳本會掃描監控日誌的資料傳送訊號,如果沒有回應則重新啟動程式
- *
- * @return void
- */
- public function writeProcess() {
-
- // 初始化proc
- $this->_initProcessLocation();
-
-
-
- // 指定進程的目錄
- $processDir = $this->processLocation . '/' . $this ->_pid;
- $processCmdFile = $processDir . '/cmd';
- $processPwdFile = $processDir . '/pwd';
-
- // 所有進程所在的目錄
- if (> !is_dir($this->processLocation)) {
- mkdir($this->processLocation, 0777);
- chmod($processDir, 0777);
- }
- $pDirObject = dir($this->processLocation);
- while ($pDirObject && (($pid = $pDirObject->read()) !== false)) {
- if ($pid == '.' || $pid == '..' || intval($pid) != $pid) {
- continue;
- }
-
- $pDir = $ this->processLocation . '/' . $pid;
- $pCmdFile = $pDir . '/cmd';
- $pPwdFile = $pDir . '/pwd';
- $pHeartFile = $pDir . 'pDir . 'pDir . /heart';
-
- // 根據cmd檢查啟動相同參數的程序
- if (is_file($pCmdFile) && trim(file_get_contents($pCmdFile)) == $command) {
- unlink( $pCmdFile);
- unlink($pPwdFile);
- unlink($pHeartFile);
-
- // 刪除目錄有快取
- usleep(1000);
-
- }
- }
-
- // 新行程目錄
- if (!is_dir($processDir)) {
- mkdir($processDir, 0777);
- chmod($processDir, 0777);
- }
-
- // 寫入指令參數
- file_put_contents($processCmdFile, $command);
- file_put_contents($processCmdFile, $command);
- file_put_contents($processSERPFile, $. ]);
-
- // 寫檔案有快取
- usleep(1000);
-
- return true;
- }
-
- /**
- * _initProcessLocation
- * 初始化
- *
- * @return void
- 🎜> }
-
- /**//
- protected function _initProcessLocation() {
-
- $this->processLocation = ROOT_PATH . '/app/data/proc';
- $this->processHeartLocation = $this->processLocation. .' . $this->_pid . '/heart';
} }
| 複製程式碼