1. 程式人生 > >如何用php語言實現實時訊息處理的後臺守護程序

如何用php語言實現實時訊息處理的後臺守護程序

用php實現後臺任務通常有2種方式,一種是cron,也就是定時任務排程了,該方式實現和部署都比較簡單,php的程式碼也能複用,能滿足很多場景的業務需求。但是該方式也有個缺點,就是cron最短也只能1分鐘排程一次,這對一些要求實時處理的業務還略顯不夠完美,那能否用php實現可以實時處理的後臺任務呢?這就是我們下面要講的第二種方式,即後臺守護程序。

php做後臺程序最主要的問題就是僵死問題,程式碼跑一段時間就會僵死或者崩潰,cron方式由於定時重新排程,所以可以規避僵死問題,但是要是不用cron而是後臺守護程序方式執行,那就要必須解決這個問題才行了。

先說明一下,這個解決方案不是我原創的,是我拿來用的,根據具體業務做了修改。具體的原理我就不詳細講了,請看原文的連結

這個解決方案很靠譜,跑了n個月,沒有重啟過。先把程式碼貼上,大家參考。

#!/usr/bin/php -q
<?php
ini_set('display_errors',0);
print "Parent : ". getmypid() . "\n";


global $pids;
$pids = Array();


// Daemonize
$pid = pcntl_fork();
if($pid){
// Only the parent will know the PID. Kids aren't self-aware
// Parent says goodbye!
print "\tParent : " . getmypid() . " exiting\n";
exit();
}


print "Child : " . getmypid() . "\n";


// Handle signals so we can exit nicely
declare(ticks = 1);


function sig_handler($signo){
global $pids,$pidFileWritten;
if ($signo == SIGTERM || $signo == SIGHUP || $signo == SIGINT){
// If we are being restarted or killed, quit all children

// Send the same signal to the children which we recieved
foreach($pids as $p){ posix_kill($p,$signo); } 

// Women and Children first (let them exit)
foreach($pids as $p){ pcntl_waitpid($p,$status); }
print "Parent : "
.  getmypid()
. " all my kids should be gone now. Exiting.\n";
exit();
}
else if($signo == SIGUSR1){
print "I currently have " . count($pids) . " children\n";
}
}
// setup signal handlers to actually catch and direct the signals
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP,  "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");


// All the daemon setup work is done now. Now do the actual tasks at hand


// The program to launch
$program = "/shell";
$arguments = array("myscript");


while(1){
// In a real world scenario we would do some sort of conditional launch.
// Maybe a condition in a DB is met, or whatever, here we're going to
// cap the number of concurrent grandchildren
if(count($pids) < 6){
$pid=pcntl_fork();
if(!$pid){
pcntl_exec($program,$arguments); // takes an array of arguments
exit();

else {
// We add pids to a global array, so that when we get a kill signal
// we tell the kids to flush and exit.
$pids[] = $pid;
}
}

// Collect any children which have exited on their own. pcntl_waitpid will
// return the PID that exited or 0 or ERROR
// WNOHANG means we won't sit here waiting if there's not a child ready
// for us to reap immediately
// -1 means any child
$dead_and_gone = pcntl_waitpid(-1,$status,WNOHANG);
while($dead_and_gone > 0){
// Remove the gone pid from the array
unset($pids[array_search($dead_and_gone,$pids)]); 

// Look for another one
$dead_and_gone = pcntl_waitpid(-1,$status,WNOHANG);
}

// Sleep for 1 second
sleep(1);
}

不好意思,原文的連結找不到了,最近谷歌上不了,找個東西真是不方便,其他搜尋引擎還不夠給力啊,這是逼我們自己搞一個啊。加油