cakePHPのシェルコマンドモードから繰り返しrequestActionを呼び出すコードを実行すると、2000回くらい繰り返したところでメモリオーバーで停止していたのを解決しました。
PHPバージョンが5.2なもので、バージョンアップすればガーベッジコレクションが効いてなんとかなるかも(PHP マニュアル >機能 >ガベージコレクション)、とも思ったのですが、お客様のサーバーだし、そもそもメモリーを使い続けるのがおかしいのでなんとかしてみます。
まず、メモリ使用量の計測
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php App::import('Vendor', 'Pear/Mail/mimeDecode'); App::uses("ItemsController", "Controller"); class CollectPriceShell extends AppShell { // モデルを使用する場合はここに記述。 public $uses = array('Item'); // メソッドを無名にする場合は "main" を用いる public function main() { $items = $this->Item->find('all'); foreach($items as $item){ $url = sprintf("/items/update_supply/%d/csv", $item['Item']['id']); $result = $this->requestAction($url); echo sprintf( '%8s %8dk : %s', ($result) ? 'Success' : 'Failed', memory_get_usage() / 1024, $url)."\n"; } } } |
結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Success 15995k : /items/update_supply/8243/csv Success 16055k : /items/update_supply/8244/csv Success 16111k : /items/update_supply/8246/csv Success 16172k : /items/update_supply/8249/csv Success 16228k : /items/update_supply/8251/csv Success 16285k : /items/update_supply/8252/csv Success 16341k : /items/update_supply/8253/csv Success 16403k : /items/update_supply/8254/csv Success 16458k : /items/update_supply/8256/csv Success 16515k : /items/update_supply/8258/csv Success 16571k : /items/update_supply/8259/csv Success 16633k : /items/update_supply/8260/csv Success 16689k : /items/update_supply/8261/csv Success 16746k : /items/update_supply/8263/csv Success 16811k : /items/update_supply/8264/csv Success 16866k : /items/update_supply/8265/csv |
どんどんメモリ使ってますね。requestActionがかなりメモリを消費する関数だって事は分かっているので、ここをもう少し低レベルなものに置き換えます。単純にControllerクラスを呼び出すのがいいでしょうが、cakePHPにはApp::usesを使った便利な呼び出しがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php App::import('Vendor', 'Pear/Mail/mimeDecode'); App::uses("ItemsController", "Controller"); class CollectPriceShell extends AppShell { // モデルを使用する場合はここに記述。 public $uses = array('Item'); // メソッドを無名にする場合は "main" を用いる public function main() { $items = $this->Item->find('all'); $ItemsController = new ItemsController; $ItemsController->constructClasses(); $ItemsController->request = new CakeRequest(); // request->is('get')とかしてるなら必要 14/06/16 追記 $ItemsController->startupProcess(); foreach($items as $item){ $url = sprintf("/items/update_supply/%d/csv", $item['Item']['id']); $ItemsController->request->params['requested'] = 1; $result = $ItemsController->update_supply($item['Item']['id']); echo sprintf( '%8s %8dk : %s', ($result) ? 'Success' : 'Failed', memory_get_usage() / 1024, $url)."\n"; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Success 15051k : /items/update_supply/8243/csv Success 15065k : /items/update_supply/8244/csv Success 15073k : /items/update_supply/8246/csv Success 15088k : /items/update_supply/8249/csv Success 15096k : /items/update_supply/8251/csv Success 15106k : /items/update_supply/8252/csv Success 15116k : /items/update_supply/8253/csv Success 15131k : /items/update_supply/8254/csv Success 15139k : /items/update_supply/8256/csv Success 15148k : /items/update_supply/8258/csv Success 15158k : /items/update_supply/8259/csv Success 15172k : /items/update_supply/8260/csv Success 15181k : /items/update_supply/8261/csv Success 15191k : /items/update_supply/8263/csv Success 15210k : /items/update_supply/8264/csv Success 15218k : /items/update_supply/8265/csv |
改善された!ちょっとづつ増えていますが、16Mくらいで安定して、増え続けるという事はありませんでした。
最初にApp::uses(“ItemsController”, “Controller”);で使用する事を宣言しておきます。App::importでもいいのですが、cakePHP2ではusesの方が使用する時だけ呼び出すのでいいっぽいです。
使用する時は$ItemsController = new ItemsController;で宣言して、いつも通り$ItemsController->action()でいいのですが、これだとbeforeFilterが実行されません。
$ItemsController->constructClasses();
$ItemsController->startupProcess();
をクラスの宣言直後に行う事で、初期化されるようになりました。
コントローラーの変数は直接操作できるので、$ItemsController->request->params[‘requested’] = 1のようにリクエスト変数をいじってrequest_actionと同じパラメータにすることもできます。