GistをDB代わりにしてブログを作ってみた
"PHP Fatal error: Allowed memory size of xxx" で落ちる前にできること
PHP Advent Calendar 2013 - 7日目
昨日は@hidenorigotoさんのBeyond MVCでした。
今日は PHPあるある的な問題に対する対策について少しばかり書きたいと思います。
※ 歯ブラシを持たせてみた *1
メモリ不足でよくやる対応
PHPで大量のデータを扱ったりすると、設定された利用可能な最大メモリ使用量を超えて
PHP Fatal error: Allowed memory size of 524288330 bytes exhausted (tried to allocate 351 bytes) in ....
というエラーが発生して対応に追われたことの経験がある人はかなり多いと思います。
そもそも環境のメモリに余裕があるならmemory_limitを調整してメモリの使用量を増やしたり
あまり余裕がない場合は暫定的対応として、該当処理部分だけ
<?php ... ini_set('memory_limit', '512M'); ...
としたり、バッチ処理だったら
$php -d "memory_limit=512M" hoge.php
としたりしますよね。
今日のお話はメモリ不足エラーになる前にできることがあるよねというお話です。
嫌な兆候を事前に察知
突然データが莫大になりメモリ不足になることもあるでしょうが、事前に使用メモリ量が増大して来るケースも多々あります。
つまり、エラーを起こしてしまう前に "最大メモリ量のxx%を超えたらログに残す" ように仕込めば慌てずに済みますよね。
とはいえ、すでに稼働しているアプリでこのような仕込みを行うためにはどうしたらよいでしょう?
フレームワークのフックポイントを利用する
Webアプリケーションの何かしらのフレームワークを使っているなら、大抵処理を終えた後に何か仕込むことができるようになっていると思います。Symfony2 だと Event Listener があるので、`kernel.response` にメモリ使用量をチェックしてログに吐くようなものを仕込んでおけばOKですね。DIでロガーを渡せば簡単に好きなロガーで書き込みもOK。
<?php ... public function checkPeakMemory() { // 512M のように M で指定されている前提なのでアレでごめんなさい list($max) = sscanf(ini_get('memory_limit'), '%dM'); $peak = memory_get_peak_usage(true) / 1024 / 1024; $used = ((int) $max !== 0)? round((int) $peak / (int) $max * 100, 2): '--'; if ($used > 85) { $this->logger->warning(sprintf('Memory peak usage warning: %s %% used. (max: %sM, now: %sM)', $used, $max, $peak)); } }
、CakePHP なら Event Manager で同じようなことができるんじゃないでしょうか(未確認)
レガシーなアプリなんだけど
継ぎはぎされた素晴らしい実績のあるレガシーコードでそんな仕組みなどない!手を入れづらい!というケースもあるでしょう。
が、PHPには auto_prepend_fileというphp.ini ディレクティブの設定があり、これで上記と同じようなコードを書いてregister_shutdown_functionで指定しておけばアプリケーションの開始時にファイルが読み込まれ、スクリプト処理が完了したとき、あるいは exit() がコールされたときに登録したメソッドが呼ばれます。
ここで ログを残すであればerror_logでざっくりと指定すれば楽ですね。
この方法のメリットは既存のコードに一切修正を加えずにアプリの処理終了時にもれなく処理を練り込むことができるという点です。今回のような使い方以外でも色々と便利な利用シーンがあると思います。
<?php register_shutdown_function(function(){ // 512M のように M で指定されている前提なのでアレでごめんなさい list($max) = sscanf(ini_get('memory_limit'), '%dM'); $peak = memory_get_peak_usage(true) / 1024 / 1024; $used = ((int) $max !== 0)? round((int) $peak / (int) $max * 100, 2): '--'; if ($used > 85) { $message = sprintf("[%s] Memory peak usage warning: %s %% used. (max: %sM, now: %sM)\n", date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']), $used, $max, $peak); error_log($message, 3, "/var/tmp/my-errors.log"); } });
もし、しきい値を超えた場合は
[2013-12-07 00:39:12] Memory peak usage warning: 87.5 % used. (max: 128M, now: 112.75M)
みたいにログに残すことができます。
横展開
他にも同様のパターンとして "Fatal error: Maximum execution time of XX seconds exceeded" も仕込んでおけますね。実際自分はメモリと実行時間の両方にしきい値を設けてしきい値を超えたらログに残すようにしています。
監視方法での注意
このとき、メールで通知とかしたくなる人もいらっしゃるかも知れませんが、運用しているサービスの特性によってはメールが飛び過ぎて簡単に死ぬのでエラーの監視は別のレイヤーで行うようにしたほうが幸せです。
さて、明日は@makiesさんです。お楽しみに!
簡単にPHPをmakeしてみた #vgadvent2013
初めての方こんにちは!!いつもお世話になっている方こんにちは!!
VOYAGE GROUP エンジニアブログ Advent Calendar 2013 の3日目担当の @brtriver です。
Zucks で広告配信システムのアレやコレをやってるエンジニアです。
冬と言えば鍋。そうPHPですね!
皆さんはどうやってPHPをmakeしてますか?
yumやapt-getを使ってるという方も多いと思います。
今回は簡単なmake方法について紹介してみます。
適当な大きさで丸める
まずは、軽量樹脂粘土を用意します。PHPなので、青と白があればとりあえず大丈夫ですね。
今回は目玉用に黒も用意してますが、なければマジックペンとかでも大丈夫です。
青は白と混ぜて良い感じの色にします。
じっくりと引き延ばしながらこねるこがコツです。
本体をmake
丸みを帯びた体のほうがかわいいので、丸みを意識して頭+鼻と足の部分を整形していきます。
make testすると尚良いんですが、大抵失敗します。
足の部分はつまようじを使って四等分していきます。
ぼてっとした短足がなんともかわいらしさを倍増させます。
アレンジしてみる
着ぐるみバージョンなんてのもほら簡単。
ほら!簡単でしょ!
みんなも Let's make PHP :)
また、Zucks では 一緒に最高のプロダクトを開発したい仲間を募集中です。
気になった方は私と一緒に粘土をこねながらお酒飲みましょう。
お気軽に @brtriver まで声をかけてくださいね。
明日のアドベントカレンダーはいつも元気な @chocopie116 です。お楽しみに!
JSONのフォーマットを読みやすくする
たとえば、
{"name":"fabpot/silex-skeleton","description":"Apre-configuredskeletonfortheSilexmicroframework","license":"MIT","type":"project","require":{"php":">=5.3.3","silex/silex":"~1.0","silex/web-profiler":"~1.0","symfony/browser-kit":"~2.3","symfony/class-loader":"~2.3","symfony/config":"~2.3","symfony/console":"~2.3","symfony/css-selector":"~2.3","symfony/debug":"~2.3","symfony/finder":"~2.3","symfony/form":"~2.3","symfony/monolog-bridge":"~2.3","symfony/process":"~2.3","symfony/security":"~2.3","symfony/translation":"~2.3","symfony/twig-bridge":"~2.3","symfony/validator":"~2.3"},"autoload":{"psr-0":{"":"src/"}},"extra":{"branch-alias":{"dev-master":"1.1.x-dev"}}}
みたいなjsonを読みたいときに
{ "name": "fabpot\/silex-skeleton", "description": "Apre-configuredskeletonfortheSilexmicroframework", "license": "MIT", "type": "project", "require": { "php": ">=5.3.3", "silex\/silex": "~1.0", "silex\/web-profiler": "~1.0", "symfony\/browser-kit": "~2.3", "symfony\/class-loader": "~2.3", "symfony\/config": "~2.3", "symfony\/console": "~2.3", "symfony\/css-selector": "~2.3", "symfony\/debug": "~2.3", "symfony\/finder": "~2.3", "symfony\/form": "~2.3", "symfony\/monolog-bridge": "~2.3", "symfony\/process": "~2.3", "symfony\/security": "~2.3", "symfony\/translation": "~2.3", "symfony\/twig-bridge": "~2.3", "symfony\/validator": "~2.3" }, "autoload": { "psr-0": { "_empty_": "src\/" } }, "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } } }
というように変換して読みたいとき
by php
$ cat /tmp/hoge.json | php -r 'echo json_encode(json_decode(file_get_contents("php://stdin")), JSON_PRETTY_PRINT);'
by python
$ cat /tmp/hoge.json | python -mjson.tool
参照: http://stackoverflow.com/questions/352098/how-can-i-pretty-print-json-from-the-command-line?page=1
pythonいいわー
Symfony 勉強会 #8 を開催しました
5/25(土)に 半年ぶりとなる Symfony勉強会 #8 を VOYAGE GROUP のajitoにて開催しました。
参加者の方の感想などのブログも上がっています。私はスタッフ側として今回の勉強会を振り返りたいと思います。
Symfonyユーザー会として今回の勉強会の目標は "Symfonyを触ってみたい人にSymfonyの魅力を伝えつつ実際に触れてもらうこと"でした。
過去開催した勉強会はどちらかというと、色々詰め込みすぎて朝早くから夜遅くまでのスパルタワークショップになったりと準備するのも大変だったり、参加者の疲労感もかなりのものになっていました。
そして、回数を重ねて行くと内容もどんどん濃いものになってきました。
今後もできるだけ勉強会の間を開けずに小さく開催することができればということを考えて、Symfonyをなんとなく知ってて触ってみたい人を対象に午後から5時間程度での開催としました。
最終的に30人弱の方が参加されました。最近の勉強会ではキャンセル率が高くて場合によってはキャンセル率が50%を超えたりすることもあるのですが、14%程度とかなり皆さんが楽しみにされていたんだろうなと思いました。
最初のセッションは基礎編と最新情報の2つ
Symfonyはよく難しいと言われますが、たぶん他のフレームワークとそんなに変わらないと思います。
Symfony独特な技術を駆使するわけでもなく、古くから実績のある一般的なMVCのような関心事の分離をごく普通に行う素直なフレームワークです。
そして、さらに開発者が開発や運用を行いやすくするための仕組み (コマンドやProfiler) が準備されていたり、何時ごろ何をリリースするかのスケジュールだけでなく、「後方互換をどこまで維持しようとするか、BCがあってもどう気づくことができるようにするか」など、symfony1系のころからの運用経験を存分に活用しコミュニティーでSymfonyの開発を進めているという特徴は他のフレームワークより一歩先を行っていると思います。
だからといってSymfonyが一番優れているということを言いたかったのではなく、Symfonyの開発で必要な知識は言語を超えてWebアプリケーションの開発において活用できるものが多いので、食わず嫌いにならずにぜひ触れてみてその世界を体験してもらいたかったということでした。
実際、私はSymfonyだけで開発しているわけでもありませんし、他のフレームワークだったとしても、言語だったとしても関心事の分離を意識した設計にすることや継承地獄にならないための手法やMockを使った軽量なユニットテストを書くことなどSymfonyを使ったときの開発と同じ手法をやっているだけだなと思います。
後藤さんには具体的な内容のリクエストはしていなかったのですが、まさに伝えてほしかった内容でさすがでした。
PHP Mentors -> 第8回 Symfony勉強会参加&基礎知識セッションスライド等
今後やりたいこと
今回のような勉強会でもまだ規模が大きいかなと思うので、もう少し軽めの勉強会を8月ぐらいでできればなと思っています。
また、Symfonyユーザー会では温泉旅行もあると思うので興味あるかたは https://groups.google.com/forum/?fromgroups#!forum/symfony-users-ja に参加を :)
謝辞
スタッフのユーザー会の皆さん!とくに今回講師をやってくださった @okapon_pon さん、いつも無茶ぶりにも関わらず刺激あるLTをしてくださる @koriym さん、会場スタッフ業でおいしいゼリーの差し入れまでしてくださった @kuromatu さん
そして、参加してくださった皆さん。ありがとうございました。
さいごに
Symfonyとは関係ないですが、現場で既存コードと戦っている人たちが集まってどうやって目の前のコードをより良くしていくかという感じの話を CodeIgniter Talk #01 - CodeIgniter Talk | Doorkeeper で少し話します。
興味があるかたは是非参加してみはどうでしょうか?
Symfony をしんふぉにゃん化 (2.3対応版)
2年前にかいてたこの記事のアップデートです。
Symfony2のエラーページのカスタマイズ - ぷぎがぽぎ
composerでインストールできるようになりました。
そして地味にまだリリースされていない2.3に対応すべくRC1で確認済みです。
ゆるふわ Symfony化
composer でしんふぉにゃん化
あなたの Symfonyプロジェクトの composer.json に以下の1行を追加し composer.phar install するだけ
"require": { .... "symfonyan/acme-symfonyan-bundle": "dev-master" }
AppKernel.phpでAcmeSymfonyanBundleを有効にする
if (in_array($this->getEnvironment(), array('dev', 'test'))) { .... $bundles[] = new Acme\SymfonyanBundle\AcmeSymfonyanBundle(); // <= 追加 }
symfonyanコマンドでインストール
$ ./app/console symfonyan:exception-install $ ./app/console symfonyan:welcome-install
prod環境で反映されないとかはキャッシュが原因の可能性が高いので php app/console ca:c --env=prod を試してみましょう。
Phalconでマイクロフレームワーク
久しぶりにPHPのエクステンションで書かれた超高速フレームワークPhalconを見たら
- 公式ページが見やすくなってた
- バージョンが1.0.0になってた
- annotationリーダーのライブラリができてた(そしてPhlconで使える)
- Microというクラスがあり、マイクロフレームワーク風に書けるようになってた
と、相変わらず斜め上まっしぐらな感じがします。
マイクロフレームワークはルーティングだけ欲しいのなら選択肢としてありなきがします。
Phalconのことだからオーバーヘッドも少ないでしょうし。
というわけでGWで遊んでみたい人のためのやってみよう記事。
Phalconをインストール
たぶん一番難しいのはここだけ。
- インストール方法: http://phalconphp.com/download
自分はmacのローカルに入れたので、以下のコマンドをぽちぽちたたいた。
$ git clone git://github.com/phalcon/cphalcon.git $ cd cphalcon/build $ sudo ./install
実際は複数のPHPのバージョンを入れている関係でinstallファイルに書かれているphpizeのパスを変更したり、--with-php-configの指定を変更して実行
コントローラーファイルを書く
requireなんていりません。だってPhalconだから。
- index.php
<?php $app = new Phalcon\Mvc\Micro(); $app->get('/', function () { echo "<h1>Welcome!</h1>"; }); $app->get('/say/welcome/{name}', function ($name) { echo "<h1>Welcome $name!</h1>"; }); $app->handle();
すっきり。
アクセスしてみる
ブラウザから以下のようなURLでアクセス。htaccessとか書いてないので、パスは_urlパラメータとして渡すのがコツ。
http://localhost:9999/index.php?_url=/say/welcome/brtriver
これは遊べそう。
マイクロな使い方のドキュメントは以下を参照。サンプルコードが多いからたぶん難しくないと思う。
さくらインターネットとかどこかのレンサパさん!
Phalconを標準で組み込んだPHPを使えるようにすると大ヒットするかもしれません。
まとめ
本当は BEAR.Sunday のルーターとしてPhalconを使えるようにしようとして久しぶりに見たのがきっかけだったり。腰据えてするならBEAR.Sundayのほうが面白いと思う。
あと、Silexの1.0がリリースされました。
5.3/5.4/5.5 どのバージョンでも動作します。また、5.4以上だとtraitで実装された機能も使えます。
これも要チェックです。