如何防止PHP進程異常退出?
背景
通常,在cli下運行的常駐後台PHP進程,可能異常退出,比如php執行過程中出現的致命錯誤,或被 kill 命令手動殺死等。如下面的php代碼:
while(1){
$content = fgets(STDIN);
if(empty($content)){
sleep(1);
}
//邏輯處理部分代碼省略
}
排查過程
我們使用register_shutdown_function來跟蹤下到底是什麼錯誤導致的進程退出。(想更多了解register_shutdown_function,請查看博文 妙用php中的register_shutdown_function和fastcgi_finish_request )加入了錯誤捕捉代碼。如下:
$is_end = false;
function catch_error(){
global $is_end;
$time = date("Y-m-d H:i:s");
$error = error_get_last();
$msg = "$time [error]";
if($is_end){
$msg .= "is_end[yes]";
}else{
$msg .= "is_end[no]";
}
if($error){
$msg .= var_export($error,1);
}
echo $msg."
";
}
register_shutdown_function("catch_error");
可是,php進程再次退出。而在日誌中並沒有記錄任何信息。說明register_shutdown_function方法根本沒有執行。是什麼導致register_shutdown_function方法沒有運行呢?在php的官方文檔中又這樣一個注釋:
Shutdown functions will not be executed if the process is killed with a SIGTERM or SIGKILL signal. While you cannot intercept a SIGKILL, you can use pcntl_signal() to install a handler for a SIGTERM which uses exit() to end cleanly.
注釋的意思是當php進程獲得SIGTERM和SIGKILL信號而退出時,是不執行register_shutdown_function方法的。可以使用pcntl_signal()方法來捕獲信息,並調用相應的處理方法。
好,那是不是信號導致我們的php進程退出呢?我們加入如下代碼:
declare(ticks = 1);
function sig_handler($signo){
$time = date("Y-m-d H:i:s");
echo $time." exit signo[{$signo}]
";
exit("");
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGQUIT, "sig_handler");
pcntl_signal(SIGILL, "sig_handler");
pcntl_signal(SIGPIPE, "sig_handler");
pcntl_signal(SIGALRM, "sig_handler");
過一段時間,發現php進程退出了,日誌中出現了如下日誌信息:
2014-11-23 18:30:06 exit signo[14]
2014-11-23 18:30:06 [error]is_end[no]
看來是sigalarm信號導致php進程退出了。這個信號是可以捕獲和處理的。這樣無關緊要的信號,我們還是忽略吧。最終的代碼如下:
declare(ticks = 1);
$is_end = false;
function catch_error(){
global $is_end;
$time = date("Y-m-d H:i:s");
$error = error_get_last();
$msg = "$time [error]";
if($is_end){
$msg .= "is_end[yes]";
}else{
$msg .= "is_end[no]";
}
if($error){
$msg .= var_export($error,1);
}
echo $msg."
";
}
register_shutdown_function("catch_error");
function sig_handler($signo){
$time = date("Y-m-d H:i:s");
if($signo == 14){
//忽略alarm信號
echo $time." ignore alarm signo[{$signo}]
";
}else{
echo $time." exit signo[{$signo}]
";
exit("");
}
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGQUIT, "sig_handler");
pcntl_signal(SIGILL, "sig_handler");
pcntl_signal(SIGPIPE, "sig_handler");
pcntl_signal(SIGALRM, "sig_handler");
while(1){
$content = fgets(STDIN);
if(empty($content)){
sleep(1);
}
//邏輯處理部分代碼省略
}
$is_end = true;
經過一段觀察,在日誌中又發現了alarm相關的日誌,但是php進程依然在。看來我們的修改有作用了。
參考
http://blogread.cn/it/article/7256?f=wb_blogread
轉載請註明來自Lenix的博客,地址http://blog.p2hp.com/archives/5279
※技術新知
TAG:PHP技術大全 |