Archive for 四月 2010
前天因為Jaceju有個要產生子程序的需求在噗浪上發問…
線上的神人們無不貢獻自己所知道的東西出來…
小弟也把之前在PEAR上看到的 System_Daemon 抓出來實做一下….
一般PHP工程師習慣都是寫 Web App 的部份, 而這篇主要講的是寫 Cli 的程式, 而且是要把程式寫成 Daemon …
而什麼是 Daemon 呢?.. 直接翻譯叫做守護神… 簡單的講, 就是在背景運作的程式…
通常要讓程式在背景運作的話, Unix 系統上有幾個作法, 像是常見的 nohup , 或是 執行命令的時候最後面加上 & ….
或是直接在執行的過程中按下 ctrl+z , 然後打 bg (background 的意思) 讓剛剛那個被暫停的程序在背景跑….
這時候應該就會有人想到了…. 那有沒有可能像是 apache, mysql 那些程式… 指令打完, 就會回到命令列, 而程式本身已經在系統裡面運行了呢?…
通常你會需要學習一些 process 管理的程式, 像是要去研讀 php manual 中的 pcntl 這個章節的內容…
了解如何讓程序複製自己一份來運作…
這過程不外乎要使用到 pcntl_fork, pcntl_wait, pcntl_waitpid 等函式…
不過當開始撰寫一個 Daemon 的時候就會遇到一些小問題..像是重複執行的問題?… 是否要有紀錄檔?. 紀錄檔要放哪?..命名?…
而在 PEAR 中的 System_Daemon 主要就是幫助我們設定 Daemon 的環境, 定義你的程序運作名稱, 把訊息寫入 Log 檔案..
而最重要的就是有一個標準架構可以讓開發人員快速的把一個寫好的 php script 變成可以背景運行的 Daemon …
假設我們現在有個需求, 每隔 10 秒要去檢查一下 /tmp/darkhero.txt 這個檔案是否存在, 存在的話.就把這個檔案的 md5 寫入 /tmp/darkhero_md5.txt
以前的話可以寫一個 script 讓然後用 nohup check.php & 的方式去跑…
而現在可以寫成 daemon 的方式…
首先我們要先用 pear 安裝一下 System_Daemon ..
不過由於 System_Daemon 目前還不是 Stable 的版本, 所以要用 pear install -f System_Daemon 才能安裝喔!!..
pear install -f System_Daemon
我們拿 System_Daemon 的範例來改一下…
#!/usr/bin/php -q
/**
* System_Daemon Example Code
*
* If you run this code successfully, a daemon will be spawned
* and stopped directly. You should find a log enty in
* /var/log/simple.log
*
*/
// Make it possible to test in source directory
// This is for PEAR developers only
// ini_set('include_path', ini_get('include_path').':..');
// Include Class
// 引入 System_Daemon , 請確認有在 pear 路徑中.
error_reporting(E_ALL);
require_once "System/Daemon.php";
// Bare minimum setup
// 設定基本環境變數
System_Daemon::setOption("appName", "check_daemon");
System_Daemon::setOption("authorEmail", "kyle@ugadigital.com");
// System_Daemon::setOption("appDir", dirname(__FILE__));
System_Daemon::log(System_Daemon::LOG_INFO, "Daemon not yet started so "."this will be written on-screen");
// Spawn Deamon!
// 開始產生為 Daemon 的部份
System_Daemon::start();
// 寫入紀錄.
// System_Daemon::getOption(); 可以取得一些定義好的環境.
// 像是 System_Daemon::getOption("logLocation") 可以取得 log 的位置跟名稱.
System_Daemon::log(System_Daemon::LOG_INFO, "Daemon: '".
System_Daemon::getOption("appName").
"' spawned! This will be written to ".
System_Daemon::getOption("logLocation"));
// Your normal PHP code goes here. Only the code will run in the background
// so you can close your terminal session, and the application will
// still run.
// 接著把你要放在背景一直跑得程式碼寫在這裡... 就會在背景一直跑....
if(!file_exists('/tmp/darkhero_md5.txt'))
touch('/tmp/darkhero_md5.txt');
while(1){
if(file_exists('/tmp/darkhero.txt') and md5_file('/tmp/darkhero.txt') != file_get_contents('/tmp/darkhero_md5.txt')){
System_Daemon::log(System_Daemon::LOG_INFO, "發現檔案內容變動, 更新 md5!!!");
$md5_string = md5_file('/tmp/darkhero.txt');
file_put_contents('/tmp/darkhero_md5.txt',$md5_string);
}
usleep(100);
}
System_Daemon::stop();
?>
接著要讓這個 php 可以跑…
chmod +x check.php
因為寫入 /var/log 相關需要有 root 的權限..所以我們用 sudo 去跑..
sudo ./check.php
接著你會發線畫面上有一些訊息, 跑玩就什麼都沒有了?…
sudo ./check.php
[Apr 23 13:09:58] info: Daemon not yet started so this will be written on-screen
[Apr 23 13:09:58] notice: Starting check_daemon daemon, output in: '/var/log/check_daemon.log'
事實上這個時候, check.php 已經變成 daemon 在背景運作囉…
這時候要是你想再跑一次.就會有錯誤喔…
sudo ./check.php
[Apr 23 13:12:27] info: Daemon not yet started so this will be written on-screen
[Apr 23 13:12:27] notice: Starting check_daemon daemon, output in: '/var/log/check_daemon.log'
[Apr 23 13:12:27] emerg: check_daemon daemon is still running. Exiting [l:1250]
[Apr 23 13:12:27] info: Process was not daemonized yet, just halting current process
為什麼呢?…因為 System_Daemon 已經在 /var/run/check_daemon/ 產生了 check_daemon.pid 來確保 daemon 只會有一個在背景…
另外同時可以看到 System_Daemon 在 /var/log/ 建立了一個以 appName 為名的 log 檔..
tail -f /var/log/check_daemon.log
[Apr 23 13:12:10] info: Changed identify to 'root':'root'
[Apr 23 13:12:10] info: Daemon: 'check_daemon' spawned! This will be written to /var/log/check_daemon.log
[Apr 23 13:12:11] info: Daemon not yet started so this will be written on-screen
[Apr 23 13:12:11] notice: Starting check_daemon daemon, output in: '/var/log/check_daemon.log'
[Apr 23 13:12:11] emerg: check_daemon daemon is still running. Exiting [l:1250]
[Apr 23 13:12:11] info: Process was not daemonized yet, just halting current process
[Apr 23 13:12:27] info: Daemon not yet started so this will be written on-screen
[Apr 23 13:12:27] notice: Starting check_daemon daemon, output in: '/var/log/check_daemon.log'
[Apr 23 13:12:27] emerg: check_daemon daemon is still running. Exiting [l:1250]
[Apr 23 13:12:27] info: Process was not daemonized yet, just halting current process
這時候我們來試試看程式是不是有在背景乖乖運作呢…
我用 echo "asdfasdfadsf" > /tmp/darkhero.txt 來更新 /tmp/darkhero.txt 的同時 也用 tail -f 監看 /var/log/check_daemon.log
就在我一更動 /tmp/darkhero.txt 的同時. log 檔也出現了這一個新的訊息..
[Apr 23 13:16:07] info: 發現檔案內容變動, 更新 md5!!!
這時候再去看一下 /tmp/darkhero_md5.txt 就會發現已經被更新囉….
之後若是要停止 check.php 的話..可以用 sudo killall -9 check.php 來停止在背景運作的 check.php 喔….
No tags
