PHP多程序初探 --- 利用多程序開發點兒東西吧
阿新 • • 發佈:2018-11-16
[原文地址:https://blog.ti-node.com/blog...]
乾巴巴地叨逼叨了這麼久,時候表演真正的技術了!
做個高階點兒的玩意吧,加入我們要做一個任務系統,這個系統可以在後臺幫我們完成一大波(注意是一大波)資料的處理,那麼我們自然想到,多開幾個程序分開處理這些資料,同時我們不能執行了php task.php後終端掛起,萬一一不小心關閉了終端都會導致任務失敗,所以我們還要實現程式的daemon化。好啦,開始了!
首先,我們第一步就得將程式daemon化了!
// 設定umask為0,這樣,當前程序建立的檔案許可權則為777 umask( 0 ); $pid = pcntl_fork(); if( $pid < 0 ){ exit('fork error.'); } else if( $pid > 0 ) { // 主程序退出 exit(); } // 子程序繼續執行 // 最關鍵的一步來了,執行setsid函式! if( !posix_setsid() ){ exit('setsid error.'); } // 理論上一次fork就可以了 // 但是,二次fork,這裡的歷史淵源是這樣的:在基於system V的系統中,通過再次fork,父程序退出,子程序繼續 // 保證形成的daemon程序絕對不會成為會話首程序,不會擁有控制終端。 $pid = pcntl_fork(); if( $pid < 0 ){ exit('fork error'); } else if( $pid > 0 ) { // 主程序退出 exit; } // 子程序繼續執行 // 給程序重新起個名字 cli_set_process_title('php master process');
加入我們fork出5個子程序就可以搞定這些任務,那麼fork出5個子程序,同時父程序要負責這5個子程序的狀態等。
// 由於*NIX好像並沒有(如果有,請告知)可以獲取父程序fork出所有的子程序的ID們的功能,所以這個需要我們自己來儲存 $child_pid = []; // 父程序安裝SIGCHLD訊號處理器並分發 pcntl_signal( SIGCHLD, function(){ // 這裡注意要使用global將child_pid全域性化,不然讀到去陣列將為空,具體原因可以自己思考下 global $child_pid; // 如果子程序的數量大於0,也就說如果還有子程序存活未 退出,那麼執行回收 $child_pid_num = count( $child_pid ); if( $child_pid_num > 0 ){ // 迴圈子程序陣列 foreach( $child_pid as $pid_key => $pid_item ){ $wait_result = pcntl_waitpid( $pid_item, $status, WNOHANG ); // 如果子程序被成功回收了,那麼一定要將其程序ID從child_pid中移除掉 if( $wait_result == $pid_item || -1 == $wait_result ){ unset( $child_pid[ $pid_key ] ); } } } } ); // fork出5個子程序出來,並給每個子程序重新命名 for( $i = 1; $i <= 5; $i++ ){ $_pid = pcntl_fork(); if( $_pid < 0 ){ exit(); } else if( 0 == $_pid ) { // 重新命名子程序 cli_set_process_title('php worker process'); // 啦啦啦啦啦啦啦啦啦啦,請在此處編寫你的業務程式碼 // do something ... // 啦啦啦啦啦啦啦啦啦啦,請在此處編寫你的業務程式碼 // 子程序退出執行,一定要exit,不然就不會fork出5個而是多於5個任務程序了 exit(); } else if( $_pid > 0 ) { // 將fork出的任務程序的程序ID儲存到陣列中 $child_pid[] = $_pid; } } // 主程序繼續迴圈不斷派遣訊號 while( true ){ pcntl_signal_dispatch(); // 每派遣一次休眠一秒鐘 sleep( 1 ); }