PHPでcronのように定期実行をするプログラムを書いた
kohkimakimoto/workerphp · GitHub
自分で使う分には最低限、必要な機能を実装できたので紹介します。以下のようなPHPファイルを記述してPHPのコマンドラインから実行するとプロセスが起動しっぱなしになりcront_time
で指定したスケジュールでジョブを定期実行してくれます。
<?php require_once __DIR__.'/vendor/autoload.php'; $worker = new \Kohkimakimoto\Worker\Worker(); // job for every minute. $worker->job("hello", ['cron_time' => '* * * * *', 'command' => function(){ echo "Hello world\n"; }]); // job runs a shell command. $worker->job("uptime", ['cron_time' => '10 * * * *', 'command' => "uptime"]); $worker->start();
ジョブはクロージャでPHPのコードとして書くか、シェルのコマンドを文字列で直接指定することができます。インストールはcomposer install
するだけですが、その他詳しい情報はリポジトリのREADMEを参照してください。内部的にジョブはプロセスをforkして実行させているので、PHPのpcntl
エクステンションがインストールされている必要があります。linuxならyumとかでインストールできると思います。
cronが普通に使える環境ならまあそれを使えばいいんですが、個人の用途でherokuの無料枠dynoを使って定期実行処理をしたかったので、こんなの作りました。
Web API
cronにない拡張機能として、簡易なWeb APIを提供するHTTPサーバを組み込んでいます。
$worker->httpServer->listen();
のように書くと、8080ポートを開きます。ポートを変更したいときは
$worker->httpServer->listen(8888, 'localhost');
のようにもかけます。HTTPサーバが立ち上がったら、
$ curl -XGET http://localhost:8080/{ジョブ名}
でジョブの情報を取得
$ curl -XPOST http://localhost:8080/{ジョブ名}
でジョブを実行することができます。また$worker->httpServer->setAPIKey()
を使えば、文字列一致だけで判定する簡易な認証機能をつけられます。
ちなみに、私は以下の様な設定でherokuのPHPスタック上で動かしています。
<?php require_once __DIR__.'/vendor/autoload.php'; date_default_timezone_set('Asia/Tokyo'); use Kohkimakimoto\Worker\Worker; $worker = new Worker(); // herokuはhttpのportを動的に割り当てるので環境変数から取得する $worker->httpServer->listen(getenv('PORT')); // apiにアクセスするときのキー $worker->httpServer->setAPIKey('vjdioaewdg49q3tg...'); // 5分毎にメモリ使用量などをログに出力する設定 $worker->stats->on(); $worker->job("hoge", ['cron_time' => '* * * * *', 'max_processes' => 1, 'command' => function(){ // ... }]); $worker->job("foo", ['cron_time' => '* * * * *', 'max_processes' => 1, 'command' => function(){ // ... }]); // 30分に一回dynoをwebからアクセスしてスリープさせないようにする。 $worker->job("wakeup_dyno", ['cron_time' => '*/30 * * * *', 'max_processes' => 1, 'command' => function(){ file_get_contents("https://my_worker_xxx.herokuapp.com/?apiKey=vjdioaewdg49q3tg..."); }]); $worker->start();
とりあえず、1日動かしっぱなしにしてみたが問題はなさそうです。