當前位置:
首頁 > 知識 > 如何防止PHP進程異常退出?

如何防止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


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 PHP技術大全 的精彩文章:

技術新知

TAG:PHP技術大全 |