ホームページ バックエンド開発 PHPチュートリアル PHP のマルチプロセス消費キューについての話

PHP のマルチプロセス消費キューについての話

Jul 28, 2020 pm 05:43 PM
php

PHP のマルチプロセス消費キューについての話

はじめに

最近、キュー mcq を使用してプロセス消費キュー データを開始する小さな関数を開発しました。その後、処理できないことが判明し、別の処理を追加しましたが、しばらくすると再び処理できなくなりました...

この方法では、毎回 crontab を変更する必要があります。ハングし、開始が間に合わず、次回 crontab が実行されるまで開始されません。 Kill はプロセスを閉じる (再起動する) ときに使用され、処理中のデータが失われる可能性があります。たとえば、次の例では、処理ロジックがスリープ プロセスであると仮定します。効果を明確に確認するために、処理時間は10 秒に拡大:

<?php
$i = 1;
while (1) {
    echo "开始第[{$i}]次循环\n";
    sleep(10);
    echo "结束第[{$i}]次循环\n";
    $i++;
}
ログイン後にコピー

スクリプトを実行した後、ループが開始されるまで待ってから、kill {$pid} をプロセスに送信します。デフォルトでは、SIGTERM 15 番の信号が送信されます。 $i がキューから取得されたとします。それが 2 になると、処理中です。プログラムに kill シグナルを送信します。キューのデータ損失と同様に、問題は比較的大きいため、次のようにする必要があります。これらの問題を解決する方法を見つけてください。

开始第[1]次循环
结束第[1]次循环
开始第[2]次循环


[1]    28372 terminated  php t.php
ログイン後にコピー

nginx プロセス モデル

このとき、nginx を思い出しました。nginx は高性能サーバーの主力として、数千ものサーバーにサービスを提供しています個人サービスの場合、彼のプロセス モデルは、以下に示すように、より古典的です:

nginx 进程模型 图片来自网络

管理者は、/path/ からマスター プロセスを通じて nginx と対話します。 to/nginx .pidnginx マスター プロセスの pid を読み取り、マスター プロセスにシグナルを送信します。マスターはさまざまなシグナルに応じてさまざまな処理を実行し、管理者に情報をフィードバックします。ワーカーはマスター プロセスからフォークされます。マスターはワーカーの管理を担当し、ビジネスは処理しません。ワーカーは特定のビジネスのハンドラーです。マスターはワーカーの終了と起動を制御できます。ワーカーが予期せず終了した場合子プロセスが終了したというメッセージをマスターが受信すると、新しいワーカー プロセスが再起動されて補完され、業務処理には影響しません。 nginx は、処理中のデータを失うことなくスムーズに終了することもでき、構成を更新するときに、nginx はオンライン サービスに影響を与えることなく新しい構成をロードできるため、リクエスト量が多い場合に特に役立ちます。

プロセス設計

nginx の高度なモデルを確認した後、mcq データの処理のニーズを満たす同様のクラス ライブラリを開発できます。単一のファイルですべてのプロセスを制御し、スムーズに終了し、子プロセスのステータスを表示できます。キューデータの受信にある程度の遅延が発生する場合に対処するため、それほど複雑にする必要はありませんが、nginx のような中断のないサービスを提供するのは面倒で時間と労力がかかり、あまり意味がありません。設計されたプロセス モデルは nginx に似ており、nginx の簡易バージョンに似ています。
PHP のマルチプロセス消費キューについての話

プロセス セマフォの設計

セマフォはプロセス間の通信方法です。比較的単純で、弱い単一の機能しかありません。プロセスにシグナルを送信すると、プロセスはシグナルに応じてさまざまな処理を実行します。

マスター プロセスが開始されたら、pid をファイル /path/to/daeminze.pid に保存します。管理者はシグナルを通じてマスター プロセスと通信します。マスター プロセスは 3 種類の以下に示すように、シグナルは異なる方法で処理されます:

SIGINT 	=> 平滑退出,处理完正在处理的数据再退出
SIGTERM => 暴力退出,无论进程是否正在处理数据直接退出
SIGUSR1 => 查看进程状态,查看进程占用内存,运行时间等信息
ログイン後にコピー

マスター プロセスは、シグナルを通じてワーカー プロセスと通信します。ワーカー プロセスは、以下に示すように 2 つのシグナルをインストールしました:

SIGINT 	=> 平滑退出
SIGUSR1	=> 查看worker进程自身状态
ログイン後にコピー

ワーカー プロセスが存在する理由 シグナルは 2 つだけインストールされており、そのうちの 1 つは SIGTERM が欠落しています。これは、マスター プロセスがシグナル SIGTERM を受信した後、SIGKILL シグナルをワーカー プロセス、およびプロセスはデフォルトで強制的に閉じられます。

ワーカー プロセスはマスター プロセスによってフォークアウトされるため、マスター プロセスは pcntl_wait を通じて子プロセスの終了イベントを待つことができます。子プロセスが終了すると、子プロセスの pid は次のようになります。返され、処理され、新しいプロセスを開始して追加します。

マスター プロセスも、pcntl_wait を介してシグナルの受信を待機します。シグナルが到着すると、-1 が返されます。この場所にはまだいくつかの落とし穴があります。詳細は後述します。

PHP でシグナルをトリガーするには 2 つの方法があります。1 つ目の方法は declare(ticks = 1); です。これは効率的ではありません。Zend が低レベルのステートメントを実行するたびに、 will go プロセス内に未処理のシグナルがあるかどうかを確認します。現在はほとんど使用されていません。PHP 5.3.0 およびそれ以前のバージョンでは、これが使用される可能性があります。

2 番目の方法は、pcntl_signal_dispatch を通じて未処理のシグナルを呼び出すことです。PHP 5.4.0 以降のバージョンが適用可能です。この関数はループ内にうまく配置できます。基本的にパフォーマンスの低下はなく、現時点での使用をお勧めします。

PHP はシグナルをインストールおよび修復します signal

PHP は pcntl_signal を通じてシグナルをインストールします。関数の宣言は次のとおりです:

bool pcntl_signal ( int $signo , [callback $handler [, bool $restart_syscalls = true ] )
ログイン後にコピー

第三个参数restart_syscalls不太好理解,找了很多资料,也没太查明白,经过试验发现,这个参数对pcntl_wait函数接收信号有影响,当设置为缺省值true的时候,发送信号,进程用pcntl_wait收不到,必须设置为false才可以,看看下面这个例子:

<?php
$i = 0;
while ($i<5) {
    $pid = pcntl_fork();
    $random = rand(10, 50);
    if ($pid == 0) {
        sleep($random);
        exit();
    }
    echo "child {$pid} sleep {$random}\n";
    $i++;
}

pcntl_signal(SIGINT,  function($signo) {
     echo "Ctrl + C\n";
});

while (1) {
    $pid = pcntl_wait($status);
    var_dump($pid);
    pcntl_signal_dispatch();
}
ログイン後にコピー

运行之后,我们对父进程发送kill -SIGINT {$pid}信号,发现pcntl_wait没有反应,等到有子进程退出的时候,发送过的SIGINT会一个个执行,比如下面结果:

child 29643 sleep 48
child 29644 sleep 24
child 29645 sleep 37
child 29646 sleep 20
child 29647 sleep 31
int(29643)
Ctrl + C
Ctrl + C
Ctrl + C
Ctrl + C
int(29646)
ログイン後にコピー

这是运行脚本之后马上给父进程发送了四次SIGINT信号,等到一个子进程推出的时候,所有信号都会触发。

但当把安装信号的第三个参数设置为false

pcntl_signal(SIGINT,  function($signo) {
     echo "Ctrl + C\n";
}, false);
ログイン後にコピー

这时候给父进程发送SIGINT信号,pcntl_wait会马上返回-1,信号对应的事件也会触发。

所以第三个参数大概意思就是,是否重新注册此信号,如果为false只注册一次,触发之后就返回,pcntl_wait就能收到消息,如果为true,会重复注册,不会返回,pcntl_wait收不到消息。

信号量和系统调用

信号量会打断系统调用,让系统调用立刻返回,比如sleep,当进程正在sleep的时候,收到信号,sleep会马上返回剩余sleep秒数,比如:

ログイン後にコピー

运行之后,按Ctrl + C,结果如下所示:

123
^Climit sleep [1] s
Ctrl + C
123
limit sleep [0] s
123
^Climit sleep [1] s
Ctrl + C
123
^Climit sleep [2] s
ログイン後にコピー

daemon(守护)进程

这种进程一般设计为daemon进程,不受终端控制,不与终端交互,长时间运行在后台,而对于一个进程,我们可以通过下面几个步骤把他升级为一个标准的daemon进程:

protected function daemonize()
{
    $pid = pcntl_fork();
    if (-1 == $pid) {
        throw new Exception("fork进程失败");
    } elseif ($pid != 0) {
        exit(0);
    }
    if (-1 == posix_setsid()) {
        throw new Exception("新建立session会话失败");
    }

    $pid = pcntl_fork();
    if (-1 == $pid) {
        throw new Exception("fork进程失败");
    } else if($pid != 0) {
        exit(0);
    }

    umask(0);
    chdir("/");
}
ログイン後にコピー

拢共分五步:

1、fork子进程,父进程退出。

2、设置子进程为会话组长,进程组长。

3、再次fork,父进程退出,子进程继续运行。

4、恢复文件掩码为0

5、切换当前目录到根目录/

第2步是为第1步做准备,设置进程为会话组长,必要条件是进程非进程组长,因此做第一次fork,进程组长(父进程)退出,子进程通过posix_setsid()设置为会话组长,同时也为进程组长。

第3步是为了不让进程重新控制终端,因为一个进程控制一个终端的必要条件是会话组长(pid=sid)。

第4步是为了恢复默认的文件掩码,避免之前做的操作对文件掩码做了设置,带来不必要的麻烦。关于文件掩码, linux中,文件掩码在创建文件、文件夹的时候会用到,文件的默认权限为666,文件夹为777,创建文件(夹)的时候会用默认值减去掩码的值作为创建文件(夹)的最终值,比如掩码022下创建文件666 - 222 = 644,创建文件夹777 - 022 = 755

掩码新建文件权限新建文件夹权限

umask(0)

666 (-rw-rw-rw-)

777 (drwxrwxrwx)

umask(022)

644 (-rw-r--r--)

755 (drwxr-xr-x)

第5步是切换了当前目录到根目录/,网上说避免起始运行他的目录不能被正确卸载,这个不是太了解。

对应5步,每一步的各种id变化信息:

操作后pidppidpgidsid

开始

17723

31381

17723

31381

第一次fork

17723

1

17723

31381

posix_setsid()

17740

1

17740

17740

第二次fork

17840

1

17740

17740

另外,会话、进程组、进程的关系如下图所示,这张图有助于更好的理解。
PHP のマルチプロセス消費キューについての話

至此,你也可以轻松地造出一个daemon进程了。

命令设计

我准备给这个类库设计6个命令,如下所示:

  1. start 启动命令

  2. restart 强制重启

  3. stop 平滑停止

  4. reload 平滑重启

  5. quit 强制停止

  6. status 查看进程状态

启动命令

启动命令就是默认的流程,按照默认流程走就是启动命令,启动命令会检测pid文件中是否已经有pid,pid对应的进程是否健康,是否需要重新启动。

强制停止命令

管理员通过入口文件结合pid给master进程发送SIGTERM信号,master进程给所有子进程发送SIGKILL信号,等待所有worker进程退出后,master进程也退出。

强制重启命令

强制停止命令 + 启动命令

平滑停止命令

平滑停止命令,管理员给master进程发送SIGINT信号,master进程给所有子进程发送SIGINT,worker进程将自身状态标记为stoping,当worker进程下次循环的时候会根据stoping决定停止,不在接收新的数据,等所有worker进程退出之后,master进程也退出。

平滑重启命令

平滑停止命令 + 启动命令

查看进程状态

查看进程状态这个借鉴了workerman的思路,管理员给master进程发送SIGUSR1信号,告诉主进程,我要看所有进程的信息,master进程,master进程将自身的进程信息写入配置好的文件路径A中,然后发送SIGUSR1,告诉worker进程把自己的信息也写入文件A中,由于这个过程是异步的,不知道worker进程啥时候写完,所以master进程在此处等待,等所有worker进程都写入文件之后,格式化所有的信息输出,最后输出的内容如下所示:

/dir /usr/local/bin/php DaemonMcn.php status
Daemon [DaemonMcn] 信息:
-------------------------------- master进程状态 --------------------------------
pid       占用内存       处理次数       开始时间                 运行时间
16343     0.75M          --             2018-05-15 09:42:45      0 天 0 时 3 分
12 slaver
-------------------------------- slaver进程状态 --------------------------------
任务task-mcq:
16345     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分
16346     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分
--------------------------------------------------------------------------------
任务test-mcq:
16348     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16350     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16358     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16449     0.75M          1              2018-05-15 09:46:40      0 天 0 时 0 分
--------------------------------------------------------------------------------
ログイン後にコピー

等待worker进程将进程信息写入文件的时候,这个地方用了个比较trick的方法,每个worker进程输出一行信息,统计文件的行数,达到worker进程的行数之后表示所有worker进程都将信息写入完毕,否则,每个1s检测一次。

其他设计

另外还加了两个比较实用的功能,一个是worker进程运行时间限制,一个是worker进程循环处理次数限制,防止长时间循环进程出现内存溢出等意外情况。时间默认是1小时,运行次数默认是10w次。

除此之外,也可以支持多任务,每个任务几个进程独立开,统一由master进程管理。

代码已经放到github中,有兴趣的可以试试,不支持windows哦,有什么错误还望指出来。

相关教程推荐:《PHP教程

以上がPHP のマルチプロセス消費キューについての話の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

CakePHP プロジェクトの構成 CakePHP プロジェクトの構成 Sep 10, 2024 pm 05:25 PM

この章では、CakePHP の環境変数、一般設定、データベース設定、電子メール設定について理解します。

Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Dec 24, 2024 pm 04:42 PM

PHP 8.4 では、いくつかの新機能、セキュリティの改善、パフォーマンスの改善が行われ、かなりの量の機能の非推奨と削除が行われています。 このガイドでは、Ubuntu、Debian、またはその派生版に PHP 8.4 をインストールする方法、または PHP 8.4 にアップグレードする方法について説明します。

CakePHP の日付と時刻 CakePHP の日付と時刻 Sep 10, 2024 pm 05:27 PM

Cakephp4 で日付と時刻を操作するには、利用可能な FrozenTime クラスを利用します。

CakePHP ファイルのアップロード CakePHP ファイルのアップロード Sep 10, 2024 pm 05:27 PM

ファイルのアップロードを行うには、フォーム ヘルパーを使用します。ここではファイルアップロードの例を示します。

CakePHP ルーティング CakePHP ルーティング Sep 10, 2024 pm 05:25 PM

この章では、ルーティングに関連する次のトピックを学習します。

CakePHP について話し合う CakePHP について話し合う Sep 10, 2024 pm 05:28 PM

CakePHP は、PHP 用のオープンソース フレームワークです。これは、アプリケーションの開発、展開、保守をより簡単にすることを目的としています。 CakePHP は、強力かつ理解しやすい MVC のようなアーキテクチャに基づいています。モデル、ビュー、コントローラー

PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 Dec 20, 2024 am 11:31 AM

Visual Studio Code (VS Code とも呼ばれる) は、すべての主要なオペレーティング システムで利用できる無料のソース コード エディター (統合開発環境 (IDE)) です。 多くのプログラミング言語の拡張機能の大規模なコレクションを備えた VS Code は、

CakePHP バリデータの作成 CakePHP バリデータの作成 Sep 10, 2024 pm 05:26 PM

Validator は、コントローラーに次の 2 行を追加することで作成できます。

See all articles