原先用PHP的Pthread多執行緒實現的支付結果回撥服務,在後期執行中出現了服務停止的問題,在學習swoole的過程中,發現可以用swoole的Process程式管理模組實現多執行緒的功能,並且使用swoole_time_tick定時器功能實現程式監控在子程式退出的時候進行重啟。
1、開發環境
Swoole版本:2.0.12
PHP版本:7.1
伺服器版本:Ubuntu 14.04 64位
2、業務場景
遊戲APP在支付後,在支付寶,微信等回撥支付結果後,將支付結果回撥給遊戲伺服器。回撥邏輯為:25 小時以內完成 8 次通知(通知的間隔頻率一般是:0s,2m,10m,10m,1h,2h,6h,15h)。第一次通知在接收到結果時同時回撥。所以另外7次間隔性回撥由7個程式分別操作。每個程式服務執行時間不一致,當前業務時間間隔各為1s,2s,30s,30s,60s,300s,600s,600s一次
3、程式碼例項
use SwooleProcess;
class MyProcess
{
public $mpid = 0; // master pid, 即當前程式的程式ID
public $works = []; // 記錄子程式的 pid
public $maxProcessNum = 7;
public $newIndex = 1;
public function __construct()
{
try {
swoole_set_process_name(` MyProcess : master`);
$this->mpid = posix_getpid();
$this->run();
$this->processWait();
} catch (Exception $e) {
die(`Error: `. $e->getMessage());
}
}
public function run()
{
//建立程式
for ($i=0; $i<=$this->maxProcessNum; $i++) {
$this->createProcess($i);
}
}
public function createProcess($index = null)
{
if (is_null($index)) {
$index = $this->newIndex;
$this->newIndex++;
}
echo date(`Y-m-d H:i:s`) . ` | createProcess index=`.$index.PHP_EOL;
$process = new swoole_process(function (swoole_process $worker) use($index) { // 子程式建立後需要執行的函式
swoole_set_process_name(" MyProcess : worker $index");
//根據程式啟用不同時間間隔的定時器 $ms為毫秒 支付回撥7次嘗試 7個程式回撥服務 每次回撥的間隔時間不一致,實行25 小時以內完成 8 次通知(通知的間隔頻率一般是:2m,10m,10m,1h,2h,6h,15h)
switch ($index) {
case 0;
$ms = 1000;
break;
case 1;
$ms = 2000;
break;
case 2;
$ms = 30000;
break;
case 3;
$ms = 30000;
break;
case 4;
$ms = 60000;
break;
case 5;
$ms = 300000;
break;
case 6;
$ms = 600000;
break;
case 7;
$ms = 600000;
break;
}
//啟用定時器
$timer=swoole_timer_tick($ms,`MyProcess::deal_pay_notify`, $index);
}, false, false); // 不重定向輸入輸出; 不使用管道
$pid = $process->start();
$this->works[$index] = $pid;
return $pid;
}
/*
* 處理支付回撥
*/
function deal_pay_notify($timmerID, $params){
echo date(`Y-m-d H:i:s`) . ` | timmerID=`.$timmerID." params=".$params.PHP_EOL;
//支付結果回撥操作
//......
}
// 重啟子程式
public function rebootProcess($pid)
{
$index = array_search($pid, $this->works);
if ($index !== false) {
//重新建立程式
$newPid = $this->createProcess($index);
echo "rebootProcess: {$index}={$pid}->{$newPid} Done
";
return;
}
throw new Exception("rebootProcess error: no pid {$pid}");
}
// 監控子程式
public function processWait()
{
//定時器每秒監控
swoole_timer_tick(1000,`MyProcess::monitor_process`, ``);
/*while (1) {
if (count($this->works)) {
$ret = Process::wait(); // 子程式退出
if ($ret) {
$this->rebootProcess($ret[`pid`]);
}
} else {
break;
}
}*/
}
//檢測程式
public function monitor_process($timmerID, $params){
foreach($this->works as $pid){
if (!Process::kill($pid, 0)) { // 0 可以用來檢測程式是否存在
$this->rebootProcess($pid); //重啟程式
echo date(`Y-m-d H:i:s`) . ` | monitor_process pid=`.$pid. ` restart`.PHP_EOL;
}
}
}
}
new MyProcess();