一、 程式守護使用場景。
後端經常會有類似這樣的場景,某個指令碼,需要不斷的重複執行,這個時候,最好有一個守護程式,幫助我們不斷地自動地拉起這些指令碼程式,讓它自動地重複執行。
在 Linux/Unix 系統下,supervisor 就是使用 python 開發的一個優秀的程式管理工具,本文嘗試使用 php 來實現類似的程式管理工具。
二、swoole 的程式管理模組。
php 的 swoole 擴充套件有一個程式管理模組,官方文件見:https://wiki.swoole.com/wiki/page/p-proces...。
參考 supervisor 的實現方式,被守護的程式是作為 supervisor 的子程式來啟動的,supervisor 透過監聽子程式的訊號,可實現對子程式的自動重啟等功能。
而 swoole 的程式管理模組就提供了程式間通訊的功能,可以實現對子程式的自動重啟功能。
三、第一個程式守護程式。
要實現對子程式的守護,需要做到兩點:
- 程式需要監聽到子程式的結束訊號,以便於重新拉起新的子程式。
- 子程式的執行環境需要獨立於父程式。
swoole 程式管理模組提供了一個 bool Process->exec(string $execfile, array $args) 方法,讓子程式蛻變成另一個系統呼叫程式,同時還能保證父程式與當前程式仍然是父子程式關係。
再透過 array Process::wait(bool $blocking = true) 方法,來等待子程式的退出訊號。
下面是使用 swoole 啟動子程式,並回收子程式資源的示例程式碼:
<?php
use Swoole\Process;
$php = "/usr/bin/env php";
$script = dirname(__DIR__) . "/task.php";
$command = "{$php} {$script}";
$process = new Process(function (Process $worker) use ($command) {
$worker->exec('/bin/sh', ['-c', $command]);
});
$pid = $process->start();
printf("啟動子程式 {$pid}\n");
while ($ret = Process::wait()) {
$pid = intval($ret["pid"] ?? 0);
printf("子程式 {$pid} 結束\n");
}
程式碼解析:
$command 變數表示需要子程式指令碼,透過 exec() 方法來啟動成一個子程式的方式執行,再透過 Process::wait() 訪求來等待 $command 這個子程式指令碼結束,並回收程式資源。
那麼,只要在收到子程式的結束訊號後,再起一個相同的子程式指令碼,即可實現對子程式的守護了。
於是,第一個守護子程式的程式實現程式碼:
<?php
use Swoole\Process;
$php = "/usr/bin/env php";
$script = dirname(__DIR__) . "/task.php";
$command = "{$php} {$script}";
do {
$process = new Process(function (Process $worker) use ($command) {
$worker->exec('/bin/sh', ['-c', $command]);
});
$pid = $process->start();
printf("啟動子程式 {$pid}\n");
} while (Process::wait());
程式碼解析:
這段程式碼只將啟動子程式的邏輯加到一個死迴圈中,好讓這個子程式指令碼能夠不斷的重啟。
四、封裝成類
為了方便重用這段程式碼,可以將這段程式碼封裝成一個簡單的類:
<?php
namespace App;
use Swoole\Process;
class Daemon
{
/** @var string */
private $command;
public function __construct(string $command)
{
$this->command = $command;
}
public function run()
{
do {
$process = new Process(function (Process $worker) {
$worker->exec('/bin/sh', ['-c', $this->command]);
});
$pid = $process->start();
} while (Process::wait());
}
}
那麼,這個 Daemon 類的使用方式如下:
<?php
use App\Daemon;
$php = "/usr/bin/env php";
$script = dirname(__DIR__) . "/task.php";
$command = "{$php} {$script}";
$daemon = new Daemon($command);
$daemon->run();
這個簡單 Daemon 類雖然能實現對單個指令碼進行重啟守護,但是,如果我們有許多個指令碼同時需要守護的,這個 Daemon 類顯然是不能夠滿足需求的。
下一篇文章 使用 swoole 實現程式的守護(二)將嘗試擴充套件這個 Daemon 類。
本作品採用《CC 協議》,轉載必須註明作者和本文連結