[php]
//保證子進程上限
if($this->_maxFork >0 && $this->_children > $this->_maxFork)
{
Yii::log("_children > ".$this->_maxFork,CLogger::LEVEL_WARNING,__METHOD__);
$this->handler(SIGCHLD);
usleep(200);
continue;
}
/**
* 監控信號
* @param object socket $clientt
* @return boolean
*/
public function handler($signo) {
Yii::log("handler {$signo} ",CLogger::LEVEL_INFO, __METHOD__);
switch(intval($signo)) {
case SIGCLD:
case SIGCHLD:
Yii::log("SIGCHLD sub proccess ",CLogger::LEVEL_TRACE, __METHOD__);
//正常退出
//declare = 1, that means one signal may be correspond multi-process die
while( ($pid = pcntl_wait($status, WNOHANG|WUNTRACED)) > 0 ) {
if (FALSE === pcntl_wifexited($status)) {
Yii::log("sub proccess {$pid} exited unormally with code {$status}",CLogger::LEVEL_WARNING, __METHOD__);
} else {
Yii::log("sub proccess {$pid} exited normally",CLogger::LEVEL_INFO, __METHOD__);
}
$this->_children--;
}
break;
case SIGINT:
case SIGQUIT:
case SIGHUP:
//異常退出
$this->_cleanup();
exit(0);
break;
default:
break;
}
}
//保證子進程上限
if($this->_maxFork >0 && $this->_children > $this->_maxFork)
{
Yii::log("_children > ".$this->_maxFork,CLogger::LEVEL_WARNING,__METHOD__);
$this->handler(SIGCHLD);
// usleep(200);
continue;
}
/**
* 監控信號
* @param object socket $clientt
* @return boolean
*/
public function handler($signo) {
Yii::log("handler {$signo} ",CLogger::LEVEL_INFO, __METHOD__);
switch(intval($signo)) {
case SIGCLD:
case SIGCHLD:
Yii::log("SIGCHLD sub proccess ",CLogger::LEVEL_TRACE, __METHOD__);
//正常退出
//declare = 1, that means one signal may be correspond multi-process die
while( ($pid = pcntl_wait($status, WNOHANG|WUNTRACED)) > 0 ) {
if (FALSE === pcntl_wifexited($status)) {
Yii::log("sub proccess {$pid} exited unormally with code {$status}",CLogger::LEVEL_WARNING, __METHOD__);
} else {
Yii::log("sub proccess {$pid} exited normally",CLogger::LEVEL_INFO, __METHOD__);
}
$this->_children--;
}
break;
case SIGINT:
case SIGQUIT:
case SIGHUP:
//異常退出
$this->_cleanup();
exit(0);
break;
default:
break;
}
}
【多進程方式注意點】
變量共享問題
因為是進程,可以理解成主進程的一個拷貝,執行是從調用 pcntl_fork() 後各主、子進程後自往後運行,避免子進程層層嵌套,一般子進程執行完後是用exit(0)來退出。 子進程如果執行過程中崩潰不會影響到主進程,也不能和主進程共享變量。
信號和select 沖突問題
pcntl_signal 注冊信號後會和 stream_select 有沖突
PHP Error[2]: stream_select(): unable to select [4]: 被中斷的系統調用 (max_fd=10)
目前沒找到解決方法,未采用 pcntl_signal 進行監控。
進程回收
子進程通過exit退出後,會變成僵屍進程,需要通過 pcntl_wait 來進行回收。
centos 測試中發現最大的進程上限是3.2萬,通過設置子進程總數,大於設定值再調用 pcntl_wait 來進行回收
socket讀寫注意
多進程情況下,會出現對同個socket句柄,有多個進程同時在讀的問題。
解決方法是讀操作在主進程裡,讀出所有數據後,拋給子進程進行執行。
文件操作注意
filesize 函數在多進程情況下會出現取不到或取到的文件大小一直不變問題
這裡采用直接調用 linux下的系統函數解決。
$file_size = @filesize($logFile);
//解決並發情況下取不到文件大小問題
if(0 == $file_size ||$this->_prevFileSize == $file_size )
{
$file_size = @exec('/usr/bin/stat -c %s '. escapeshellarg($logFile));
clearstatcache();
}
壓力測試後1000的並發可達到350每秒的請求。應該還可優化。