使用 SWOOLE 實現程式的守護(一)

Kingmax發表於2019-07-31

一、 程式守護使用場景。

後端經常會有類似這樣的場景,某個指令碼,需要不斷的重複執行,這個時候,最好有一個守護程式,幫助我們不斷地自動地拉起這些指令碼程式,讓它自動地重複執行。
在 Linux/Unix 系統下,supervisor 就是使用 python 開發的一個優秀的程式管理工具,本文嘗試使用 php 來實現類似的程式管理工具。

二、swoole 的程式管理模組。

php 的 swoole 擴充套件有一個程式管理模組,官方文件見:https://wiki.swoole.com/wiki/page/p-proces...
參考 supervisor 的實現方式,被守護的程式是作為 supervisor 的子程式來啟動的,supervisor 透過監聽子程式的訊號,可實現對子程式的自動重啟等功能。
而 swoole 的程式管理模組就提供了程式間通訊的功能,可以實現對子程式的自動重啟功能。

三、第一個程式守護程式。

要實現對子程式的守護,需要做到兩點:

  1. 程式需要監聽到子程式的結束訊號,以便於重新拉起新的子程式。
  2. 子程式的執行環境需要獨立於父程式。

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 協議》,轉載必須註明作者和本文連結
看看自己是不是一個靠譜的程式設計師,來做題試試。job.xyh.io

相關文章