たとえば、メールを送信する場合、メール サーバーへの接続に 5 ~ 10 秒、またはそれ以上かかることがよくあります。成功したプロンプト メッセージがユーザーに送信され、電子メールの送信操作がバックグラウンドでゆっくりと処理されるため、明らかにユーザー エクスペリエンスが向上します。
同様のニーズを実現するために、Web プロジェクトでは、よく知られた製品である MemcacheQ、RabbitMQ などのメッセージ キュー (Message Queue) を使用するのが一般的な実装方法です。
率直に言うと、メッセージ キューは最も単純な先入れ先出しキューであり、キューのメンバーはテキストの一部です。メッセージ キューは非常に単純であるため、メッセージ キューを保持すると、これは単に電子メールを送信するタスクであるため、少し混乱するように感じます。これにより、多くの問題が発生します。
メッセージ キューは文字列型のみを格納できます。電子メールの送信などの「タスク」をメッセージ キュー内の「メッセージ」に変換するにはどうすればよいですか?
メッセージ キューはデータの保存と入力のみを担当し、プログラム自体を実行することはできません。メッセージキューからデータを一つずつ取り出し、タスクに変換して実行します。
メッセージ キューがいつデータを生成するかを予測できないため、タスク実行プログラムには、バックグラウンドに常駐するデーモン プロセスであるメッセージ キューを監視する機能も必要です。
一般的な Web アプリケーション PHP は CGI モードで実行され、メモリに常駐することはできません。 PHP には CLI モードもあることはわかっていますが、PHP CLI を使用してデーモン プロセスを実装できますか?また、それはどの程度効率的ですか?
デーモンが実行されているとき、Web アプリケーションはバックグラウンド デーモンと対話して、プロセスを開始/終了する機能を実現し、プロセスの実行ステータスを取得できますか?
上記の質問に対して、私がこれまでに見つけた最良の答えは、PHP からのものではなく、Ruby プロジェクト Resque からのものです。それはまさに、Resque が問題を明確かつシンプルに解決しているからです。バックグラウンド タスクによって引き起こされる問題に対処するため、Resque の設計は Python、php、NodeJs、および他の言語 (Python の pyres や PHP の php-resque など) にもクローン化されています。Resque 実装にはさまざまな言語バージョンがあります。もちろん、このブログでは、php-resque を使用してバックグラウンド タスクを実行する方法を説明する例として PHP バージョンを使用します。一部の詳細は Ruby バージョンと異なる場合がありますが、この記事では PHP バージョンが優先されます。
Resque はこれらの問題を次のように解決します:
実際、上記の問題からわかるように、1 つのメッセージキューだけではすべての問題を解決することはできず、介入するには新しい役割が必要です。 Resque では、バックグラウンド タスクは 3 つの役割によって完了するように抽象化されます。
ジョブ | タスク: ジョブは、バックグラウンドで完了する必要があるタスクです。たとえば、この記事の例として電子メールの送信を抽象化できます。ジョブに。 Resque では、ジョブはクラスです。
Queue: これは上記のメッセージ キューであり、Resque ではキューは Redis によって実装されます。 Resque は、キューへのジョブの挿入/キューからのジョブの削除などの機能を実装できるシンプルなキュー マネージャーも提供します。
ワーカー | エグゼキューター: キューからジョブを取り出して実行する責任を負い、デーモン プロセスとしてバックグラウンドで実行できます。
この分割に基づいて、Resque でのバックグラウンド タスクの基本的なプロセスは次のようになります:
バックグラウンド タスクを独立したクラスとして記述し、このクラスがジョブになります。
バックグラウンド プログラムを使用する必要がある場合、システムはジョブ クラスの名前と必要なパラメーターをキューに入れます。
コマンドラインからワーカーを開き、ワーカーが処理する必要があるキューをパラメータで指定します。
ワーカーはデーモンプロセスとして実行され、キューを定期的にチェックします。
キューにジョブがある場合、ワーカーはジョブを取り出して実行します。つまり、ジョブ クラスをインスタンス化し、クラス内のメソッドを実行します。
これで、バックグラウンドタスクを完了できます。
Resque には、もう 1 つの非常に重要な設計があります。ワーカーは 1 つまたは複数のキューを処理でき、ワーカーのプロセス/スレッドの数を増やすことでキューの実行速度を高速化できます。
なお、php-resqueはプロセスの開発と管理を伴うため、phpのPCNTL関数を使用するため、Linuxでのみ動作し、phpはPCNTL関数をコンパイルする必要があることに注意してください。 。 Windows を使用して同じ作業を実行したい場合、PHP の他の言語バージョンは Windows でのバックグラウンド タスクには非常に適していないことがわかります。
Ubuntu12.04LTS を例に挙げます。apt を使用して Ubuntu によってインストールされた PHP は、デフォルトで何も設定せずに PCNTL 関数をコンパイルします。
apt-get install redis-server
apt-get install curlcd /usr/local/bincurl -s http://getcomposer.org/installer | phpchmod a+x composer.pharalias composer='/usr/local/bin/composer.phar'
apt-get install git git-corecd /opt/htdocsgit clone git://github.com/chrisboulton/php-resque.gitcd php-resquecomposer install
class PHP_Job{ public function perform() { sleep(120); fwrite(STDOUT, 'Hello!'); }}
在Resque的设计中,一个Job必须存在一个perform方法,Worker则会自动运行这个方法。
php-resque也给出了最简单的插入队列实现 demo/queue.php:
if(empty($argv[1])) { die('Specify the name of a job to add. e.g, php queue.php PHP_Job');}require __DIR__ . '/init.php';date_default_timezone_set('GMT');Resque::setBackend('127.0.0.1:6379');$args = array( 'time' => time(), 'array' => array( 'test' => 'test', ),);$jobId = Resque::enqueue('default', $argv[1], $args, true);echo "Queued job ".$jobId."\n\n";
在这个例子中,queue.php需要以cli方式运行,将cli接收到的第一个参数作为Job名称,插入名为'default'的队列,同时向屏幕输出刚才插入队列的Job Id。在终端输入:
php demo/queue.php PHP_Job
结果可以看到屏幕上输出:
Queued job b1f01038e5e833d24b46271a0e31f6d6
即Job已经添加成功。注意这里的Job名称与我们编写的Job Class名称保持一致:PHP_Job
php-resque同样提供了查看Job运行状态的例子,直接运行:
php demo/check_status.php b1f01038e5e833d24b46271a0e31f6d6
可以看到输出为:
Tracking status of b1f01038e5e833d24b46271a0e31f6d6. Press [break] to stop. Status of b1f01038e5e833d24b46271a0e31f6d6 is: 1
我们刚才创建的Job状态为1。在Resque中,一个Job有以下4种状态:
Resque_Job_Status::STATUS_WAITING = 1; (等待)
Resque_Job_Status::STATUS_RUNNING = 2; (正在执行)
Resque_Job_Status::STATUS_FAILED = 3; (失败)
Resque_Job_Status::STATUS_COMPLETE = 4; (结束)
因为没有Worker运行,所以刚才创建的Job还是等待状态。
这次我们直接编写demo/resque.php:
<?php date_default_timezone_set('GMT'); require 'job.php'; require '../bin/resque';
可以看到一个Worker至少需要两部分:
可以直接包含Job类文件,也可以使用php的自动加载机制,指定好Job Class所在路径并能实现自动加载
包含Resque的默认Worker: bin/resque
在终端中运行:
QUEUE=default php demo/resque.php
前面的QUEUE部分是设置环境变量,我们指定当前的Worker只负责处理default队列。也可以使用
QUEUE=* php demo/resque.php
来处理所有队列。
运行后输出为
#!/usr/bin/env php*** Starting worker
用ps指令检查一下:
ps aux | grep resque
可以看到有一个php的守护进程已经在运行了
1000 4607 0.0 0.1 74816 11612 pts/3 S+ 14:52 0:00 php demo/resque.php
再使用之前的检查Job指令
php demo/check_status.php b1f01038e5e833d24b46271a0e31f6d6
2分钟后可以看到
Status of b1f01038e5e833d24b46271a0e31f6d6 is: 4
任务已经运行完毕,同时屏幕上应该可以看到输出的Hello!
至此我们已经成功的完成了一个最简单的Resque实例的全部演示,更复杂的情况以及遗留的问题会在下一次的日志中说明。