1. 程式人生 > >php高階應用之程序控制及程序間通訊

php高階應用之程序控制及程序間通訊

很少有用php寫服務的,然而有些場景又要求能有一個這樣的伺服器程式,它能夠與php無縫結合,並且提供高可靠靠效能的服務,並且提供現有架構所沒有的一些高階特性,例如支援自定義協議,支援長連線等等。PPM(PHP-Process-Manager)是我用PHP開發的一款程序管理框架,集成了socket服務功能,無需安裝nginx、apache、php-fpm,只需要安裝php-cli即可。支援libevent事件輪詢庫,支援服務平滑重啟,支援磁碟檔案監控及自動更新,支援各種協議包括自定義協議,最主要的由於是用php寫的,它能與php無縫結合,非常適合用php寫後端業務邏輯。

下面我講下PPM程序控制與管理的做法

為了充分發揮伺服器多核的優勢,socket服務都會採用多執行緒及多程序的模型對外提供服務。用php寫服務必然涉及到程序管理或這執行緒管理方面的東西,由於PPM使用的是多程序模型,這裡主要講下php程序控制與管理方面的內容。

1、如何成為daemon程式?

2、如何實現多程序?

3、如何監控子程序是否退出?

4、程序間如何通訊?

以下都是在linux環境中(windows不支援php多程序),並且是在php-cli模式下。

1、如何成為daemon程式

廢話不多說,直接上程式碼

  1.         // 設定umask

  2. umask(0);

  3. // fork一次

  4. $pid = pcntl_fork();

  5. if(-1 == $pid)

  6. {

  7. // 出錯退出

  8. exit("Daemonize fail ,can not fork");

  9. }

  10. elseif($pid > 0)

  11. {

  12. // 父程序,退出

  13. exit(0);

  14. }

  15. // 子程序使之成為session leader

  16. if(-1 == posix_setsid())

  17. {

  18. // 出錯退出

  19. exit("Daemonize fail ,setsid fail");

  20. }

  21.         // 現在已經是daemon程序了

2、如何實現多程序

類似上面使用pcntl_fork函式,也不多說,看程式碼

  1. $pid = pcntl_fork();

  2. if($pid > 0)

  3. {

  4. // 父程序

  5. }

  6. elseif(0 == $pid)

  7. {

  8. // 子程序

  9. }

  10. else

  11. {

  12. // 出錯

  13. }

3、如何監控子程序是否退出

php監控子程序退出有幾種辦法

1、監聽SIGCHLD訊號

在linux系統中,一個程序終止或者停止時,它的父程序會收到一個SIGCHLD訊號,在php中可以用

pcntl_sigwaitinfo、pcntl_sigtimedwait、pcntl_signal等函式都檢測到此訊號。父收到SIGCHLD訊號,說明有子程序退出了。要注意的是訊號可能會重疊,當有多個SIGCHLD訊號到達父程序時,父程序可能只檢測到一個SIGCHLD訊號。需要迴圈用pcntl_wait/pcntl_waitpid函式檢測到底有幾個子程序退出以及退出的狀態。

一個監控SIGCHLD訊號的示例

  1. pcntl_sigtimedwait(array(SIGCHLD), $siginfo);

  2. while(($pid = pcntl_waitpid(-1, $status, WUNTRACED | WNOHANG)) != 0){

  3. // 退出的子程序pid

  4. if($pid>0){

  5. // pid為$pid的子程序退出了,這裡可以重新pcntl_fork一個新的子程序

  6. }

  7. else{

  8. // 出錯了

  9. }

  10. }

上面這個例子程序會阻塞在pcntl_sigtimedwait上,同時我們可以用pcntl_sigtimedwait監聽更多的訊號,例如同時監聽SIGINT終止訊號來實現整個服務的停止,同時監聽SIGHUP來實現服務的平滑重啟等等

2、直接呼叫pcntl_wait/pcntl_waitpid監控

  1. while(($pid = pcntl_waitpid(-1, $status, WUNTRACED)) != 0){

  2. // 退出的子程序pid

  3. if($pid>0){

  4. // pid為$pid的子程序退出了,這裡可以重新pcntl_fork一個新的子程序

  5. }

  6. else{

  7. // 出錯了

  8. }

  9. }

這裡和上面的例子少了一個pnntl_sigtimedwait呼叫,直接使用pcntl_waitpid,注意pcntl_waitpid第三個引數沒有傳WNOHANG,則整個程序會阻塞在pcntl_waitpid上。

3、在程序間建立socket或者管道

在程序間建立socket或者管道,然後用select等IO複用技術監聽socket或者管道可讀,如果可讀但是沒有讀出資料,那麼說明程序間的socket或者管道已經斷開了,很可能對方程序已經退出了。

  1. // 建立管道

  2. $channel = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);

  3. stream_set_blocking($channel[0], 0);

  4. stream_set_blocking($channel[1], 0);

  5. // 建立子程序

  6. $pid = pcntl_fork();

  7. // 父程序

  8. if($pid > 0)

  9. {

  10. fclose($channel[1]);

  11. $write = $e = null;

  12. while(1)

  13. {

  14. $read = array($channel[0]);

  15. if(@stream_select($read, $write, $e, 1))

  16. {

  17. foreach($read as $channel_to_check)

  18. {

  19. if('' == fread($channel_to_check))

  20. {

  21. // $channel_to_check(這裡是channel[0])對應程序可能退出了

  22. }

  23. }

  24. }

  25. }

  26. }

  27. // 子程序

  28. elseif($pid === 0)

  29. {

  30. fclose($channel[0]);

  31. }

上面的例子整個程序會阻塞在stream_select呼叫上面。建立socket監聽可讀事件,不侷限與父子程序間監控程序退出,任何程序間建立連結後,都可以實現監控程序退出事件,而且程序間可以通過這個連結實現程序間通訊。

4、定時傳送訊號0檢測

  1. while(1)

  2. {

  3. // 使用posix_kill需要當前程序所有者與被檢測$pid所有者是同一個或者當前程序所有者擁有足夠的許可權

  4. if(!posix_kill($pid, 0))

  5. {

  6. // 程序$pid已經退出了

  7. }

  8. sleep(1);

  9. }

這個方法最簡單,只要有許可權,它可以檢測任何程序是否存活。缺點是不能及時的檢測到程序退出。

4、程序間如何通訊

PHP可用的程序間通訊方法很多

1、共享記憶體 shm_*、shmop_*   // 注意多程序寫的時候要考慮互斥

2、訊息佇列msg_*                         // 直接使用系統的訊息佇列,簡單高效,不用考慮互斥問題

3、訊號量sem_*                             // 用於互斥使用某個資源

4、管道 PIPE管道、全雙工管道、FIFO命名管道 

5、socket 命名、無名、unix socket                       

等等..

具體使用哪種,看實際需要而定。