PHPフレームワークのベンチマーク一覧の記事を拝見し、
Laravel5が遅いという結果が出てしまいちょっとへこみました。
ま、まぁハロワの早さがフレームワークの優劣じゃないし(震え声
ハロワ程度でもちょっとメモリとか多く使っちゃうだけだし(震え声
などと色々理由を付けて自分の涙を無かったことにしています。
一口に速度といっても2つの指標があって、実行速度と開発速度があって
Laravelは開発速度に重きを置かれているということなので、
こういう差がでちゃうのも仕方ないんじゃないかなーと思っています。
ちなみに、今度日本で初めてLaravelの本が出るらしいですね。ちょっと気になっています。
Laravelエキスパート養成読本[モダンなPHP開発を実現するノウハウ満載!] (Software Design plus)
さて本題に戻って、Laravelでのバッチ開発まわりに関して、
公式ドキュメントを読んでも、どうも的を射なかったのでまとめてみます。
目標
Laravelフレームワークを使用したバッチ処理の開発、cronスケジューリング
主要技術・環境
Laravel5
実装
バッチの実装
まずはバッチクラスの作成をartisan
コマンドにて行います。
1 2 | $ php artisan make :console TestCommand -- command = 'batchtest' Console command created successfully. |
クラス定義が簡単でいつも助かります。
上記コマンドを叩くことで、 TestCommand
というクラスファイルが自動で作成され、
batchtest
というコマンドが用意され、 $ php artisan batchtest
で実行できるようになります。
さて、ファイルができる場所ですが
project/app/Console/Commands/TestCommand.php
にできます。
早速、中身を確認してみましょう。
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | <!--?php namespace AppConsoleCommands; use IlluminateConsoleCommand; use SymfonyComponentConsoleInputInputOption; use SymfonyComponentConsoleInputInputArgument; class TestCommand extends Command { /** * The console command name. * * @var string */ protected $name = 'batchtest' ; /** * The console command description. * * @var string */ protected $description = 'Command description.' ; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function fire() { // } /** * Get the console command arguments. * * @return array */ protected function getArguments() { return [ [ 'example' , InputArgument::REQUIRED, 'An example argument.' ], ]; } /** * Get the console command options. * * @return array */ protected function getOptions() { return [ [ 'example' , null, InputOption::VALUE_OPTIONAL, 'An example option.' , null], ]; } } </pre--> <p>見た通りな感じですが、 fire() がメインの動作関数ですね。<br> <code>fire()</code> を編集してみましょう。とりあえず実行確認の echo してみます。</p> <pre class = "brush: php; title: ; notranslate" title= "" > public function fire() { echo 'コニチワ' ; } </pre> <p>コニチワします。<br> では実行してみましょう。</p> <pre class = "brush: bash" >$ php artisan batchtest exception 'InvalidArgumentException' with message 'Command "batchtest" is not defined.' in ~~~ </pre> <h5>怒られた!</h5> <p>コマンドを登録しないといけないようで。<br> <code>app/Console/Kernel.php</code> に、下記の通りコマンドクラス定義を追加する必要があります。<br> ここまで<code>artisan</code>コマンドで追加してくれてもいいと思うのだけどなぁ。</p> <pre class = "brush: bash; title: ; notranslate" title= "" > protected $commands = [ 'AppConsoleCommandsInspireCommand' , 'AppConsoleCommandsTestCommand' // 追加 ]; </pre> <p><code>TestCommand</code> クラスを呼び出すよう定義して <code>batchtest</code> コマンドを有効化します。<br> で、再度実行してみます。</p> <pre class = "brush: bash; title: ; notranslate" title= "" >$ php artisan batchtest exception 'RuntimeException' with message 'Not enough arguments.' in ~~~ </pre> <h5>怒られた!</h5> <p><code>getArguments()</code> がデフォルトでは引数を一つ受けないとエラーを吐くようになっているようです。<br> 引数を取る必要がなければ関数まるごと削除してしまって良いようです。<br> とりあえず適当な引数をつけて実行してみましょう。</p> <pre class = "brush: bash" >$ php artisan batchtest "example" コニチワ </pre> <h5>コニチワ!</h5> <h4>引数を受けとりたい</h4> <p>引数を受けて処理を分けるというのも多くあるので、そこまで触ってみます。<br> そもそも初期設定の下記はどういう意味かというと、</p> <pre class = "brush: bash; title: ; notranslate" title= "" > protected function getArguments() { return [ [ 'example' , InputArgument::REQUIRED, 'An example argument.' ], // 一つの引数を受け取る // ['プログラムで受ける変数名', '必須入力か否か', '引数の説明'] ]; } </pre> <p><code>getArguments()</code> の返り値配列の要素数が引数の数。<br> 中の配列が「変数名、必須引数か、引数の説明」となっているので、<br> 初期設定では、「引数を一つ必須入力、受け取ったら変数名を’example’とする」みたいな感じです。</p> <p>デフォルト引数のような使い方をしたい場合は、 <code>InputArgument::REQUIRED</code> を <code>null</code> とすればいいようです。</p> <pre class = "brush: bash" > return [ [ 'lang' , null, 'choose language. default = "ja" ], ]; </pre> <p>これで、「引数一つ自由入力、受け取ったら変数名を<code>’lang’</code>とする」という感じでしょうか。<br> で、どうやって引数を受け取るかですが、 <code>fire()</code> 内で <code> $this ->argument( '変数名' )</code> で使えるようです。<br> <code>__constract()</code> 内で受け取れないのがちょっと勿体無い感じがしますねぇ。<br> ということで、処理分けは以下のようにできるようですね。</p> <pre class = "brush: bash" > public function fire() { $lang = empty ( $this ->argument( 'lang' )) ? 'ja' : $this ->argument( 'lang' ); switch ( $lang ){ case 'ja' : echo "コニチワn" ; break ; case 'us' : echo "ハローn" ; break ; default : ~~ </pre> <p>動かしてみましょう。</p> <pre class = "brush: bash; title: ; notranslate" title= "" >$ php artisan batchtest コニチワ<p></p> <p>$ php artisan batchtest us ハロー</p> <p>$ php artisan batchtest hoge สวัสดี </p></pre> <h5>สวัสดี!!</h5> <p><code> empty ()</code> じゃなくて <code> strlen ()==0</code> で受けるべきかな?まあ用途によって。</p> <p>cron設定</p> <p>バッチを作成するたびにサーバにSSHログインしてcronの設定をしないといけないのは面倒ですね。<br> 複数台全てにcrontab設定するのは骨が折れる。<br> Laravelでは、ソースレベルでコマンド実行のスケジューリングを設定できて素敵です。<br> crontabに下記を設定します。</p> <pre class = "brush: bash" >* * * * * php /path/to/project/artisan schedule:run 1>> /dev/null 2>&1 </pre> <p>すると、毎分バッチ管理スケジューラを起動して、設定にマッチするものを実行するという感じ。<br> 公式ドキュメントには「これ以上簡単にできません!」との記述が。</p> <h5>多分これが一番簡単だと思います。</h5> <pre class = "brush: bash" > protected function schedule(Schedule $schedule ) { $schedule ->command( 'inspire' ) ->hourly(); $schedule ->command( 'batchtest' )->dailyAt( '7:00' ); // 追加 } </pre> <p>これだけで毎朝7時にコニチワできます。<br> 引数をつけたい場合は <code>command( 'batchtest us' )</code> とすればいいだけですね。<br> バッチが多くなればなるほど、管理の容易さが感じられそう。</p> <h3>結果・考察</h3> <p>毎朝7時にコニチワする定期実行バッチができました。</p> <p>同じプログラムを複数サーバにデプロイする時は、<br> サーバのホスト名を見て、バッチサーバでだけ実行するとか、web01だけで実行するなんて事も可能ですね。<br> PHP処理ならではの細かい設定を入れられて管理のしやすいcron、面白いです。</p> <script type= "text/javascript" >window.NREUM||(NREUM={});NREUM.info={ "beacon" : "bam.nr-data.net" , "licenseKey" : "NRJS-969e521f365eb8162cd" , "applicationID" : "1072048185" , "transactionName" : "ZFEGZBZZD0QCWxAPV10bJVMQUQ5ZTEsNCF9fUUlAC0sV" , "queueTime" :0, "applicationTime" :1432, "atts" : "SBYFEl5DHEo=" , "errorBeacon" : "bam.nr-data.net" , "agent" : "" }</script> |