結構重要なことだと思うので、ちょっと挑発的なタイトルになってしまいました。
akiyan.comのQdmailは使うときだけnewするかvar $is_qmailを変更しよう (特にCakePHPで使う場合)という記事を見て、自分の環境で試したところ性能に無視できない変化が出たのでエントリ。
Qdmailって?
Qdmail – PHP::Mail Library , Quick and Detailed for Multibyteを参照。
本家の説明から引用すると、
Qdmailとは、PHPのマルチバイト環境(特に日本語)にて、「文字化けしない」「簡単に”デコメ(デコメール)”やHTMLメール等の電子メールを送信することができる」メールクラスライブラリです。文字化け完全制覇を目指しています。
CakePHPのコンポーネントとしても動作可能です。(1.1.3bより主要5フレームワーク対応。)
「初心者には簡単に、上級者には多彩に」のポリシーのもとに開発しました。送り方が複雑だと例え多機能でも設定が難しく使いこなせないこともありますが、Qdmailは、使いやすさを考えて制作しています。
設定次第では、日本語だけでなく各国語対応ができる(はず)。
PHPには、”mb_send_mail”というメール関数もありますが、残念ながら、機能が限定的です。多くのマルチバイト圏言語の人々は、自国語でメールを送ることに、かなり苦労しているようです。
Pear:Mailもいいのですが、設定項目が多く、「簡単に」ということが難しいと思います。
とのことです。
説明にある通り、非常に簡単にメール送信ができるPHPのライブラリであり、特に変更などせずにCakePHPのComponentとして動作するので私も愛用させて頂いてます。
何が問題?
akiyanさんのエントリ(Qdmailは使うときだけnewするかvar $is_qmailを変更しよう (特にCakePHPで使う場合))がわかりやすく解説しているので引用。
最近CakePHPでメール受信系のシステムを構築したときのこと。sendmailのログを/var/log/maillogで追っかけていたら「なぜかサイト(Apache)にアクセスがあるたびにmaillogにログが1行挿入される」という現象を偶然確認しました。もちろん、アプリからメール送信は行っていません。
今回の場合、CakePHPのコンポーネントとしてQdmailを利用していたので、アプリの起動時に必ずQdmailのコンストラクタが走っていました。それでアクセス毎にmaillogが発生していたんですね。他のフレームワークではどうなるかは未確認です。
問題の発生条件とその影響を自分なりに要約すると以下のようになります。
- CakePHPのコントローラーの$componentsにてQdmailをincludeしている場合に起こる
- 該当するページのアクセスがある度にQdmailのコンストラクタが呼ばれ、maillogへの書き込み及びsystem関数でsendmailが実行される
書き方によるので影響のないアプリもあると思うんですが、CakePHPのComponentとして使う場合、ほとんどが条件を満たしているかと思います。
まして、私の場合よく使うコンポーネントはAppControllerの$componentsでincludeしていたため、影響範囲は全画面に及んでしまっていました。
実際どのくらいの影響になる?
Apache Benchにて、計測してみました。(Apache Benchの説明はこちら)
ab -n 1000 -c 10 http://workspace/hoge
対策前
Concurrency Level: 10
Time taken for tests: 244.928 seconds
Complete requests: 1000
Failed requests: 987
(Connect: 0, Receive: 0, Length: 987, Exceptions: 0)
Write errors: 0
Total transferred: 26952546 bytes
HTML transferred: 26601059 bytes
Requests per second: 4.08 [#/sec] (mean)
Time per request: 2449.276 [ms] (mean)
Time per request: 244.928 [ms] (mean, across all concurrent requests)
Transfer rate: 107.46 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 2.6 0 30
Processing: 1114 2445 364.8 2459 3808
Waiting: 1065 2333 354.7 2351 3594
Total: 1114 2445 365.0 2459 3808
対策後
Concurrency Level: 10
Time taken for tests: 227.431 seconds
Complete requests: 1000
Failed requests: 788
(Connect: 0, Receive: 0, Length: 788, Exceptions: 0)
Write errors: 0
Total transferred: 26960245 bytes
HTML transferred: 26608929 bytes
Requests per second: 4.40 [#/sec] (mean)
Time per request: 2274.305 [ms] (mean)
Time per request: 227.431 [ms] (mean, across all concurrent requests)
Transfer rate: 115.76 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 4.0 0 57
Processing: 694 2269 432.3 2293 3793
Waiting: 679 2176 416.4 2194 3608
Total: 694 2269 432.7 2293 3793
結構違う・・・
ここで見る項目は、Requests per second(1秒間に捌けたリクエスト数)とTime per request(1リクエストあたりにかかった時間)の2つです。
Request per secondは、
(対策前)Requests per second: 4.08 [#/sec] (mean)
(対策後)Requests per second: 4.40 [#/sec] (mean)
と、対策後の方が10%ほど向上しています。
Time per requestの方はというと
(対策前)Time per request: 2449.276 [ms] (mean)
(対策後)Time per request: 2274.305 [ms] (mean)
とこちらも対策後の方が10%ほど向上しています。
かなりのトラフィックを稼いでいるサイトになると、10%の差は貴重なリソースの無駄遣いとなってしまいますね。
(蛇足:ローカル開発環境のゴチャゴチャいろんなものが動いてる中での計測ですのでかなりパフォーマンスが悪いのは無視してください・・・)
どういう対策をすればよい?
akiyanさんのエントリ(Qdmailは使うときだけnewするかvar $is_qmailを変更しよう (特にCakePHPで使う場合))ではQdmailのソースコードを直接書き換えています。
修正方法は、ソースコードの直接書き換えとしました。今回の環境ではsendmailを使うので、QdmailBase::$is_qmailのデフォルト値をfalseとしました。
この方法が最もスマートかと思いますが、私はライブラリを書き換えるのはなるべく避けるようにしてます。
そのため、メール送信処理を行う場合のみnew Qdmail()でインスタンスを取得して使用するようにしました。
少なくとも、現状ではAppControllerでのinludeはご法度かと。
終わり。
冒頭でも述べたとおり、Qdmailはとてもシンプルかつ高機能で強力なライブラリです。
少なくとも、標準であるMailComponentがマルチバイト文字に弱い現状では、CakePHPによるメール送信のベストだと思います。
今回の件に限らず、サイトのパフォーマンスアップやWEBページの表示速度の改善はそのサイトのステークホルダー全員にメリットのある方法です。
私は、パフォーマンスに気を遣いながら書くコードは自然と綺麗なものになっていくと思っていますので、もちろんプログラマーにもメリットがあります。
何より、そういったことに気を遣いながらの作業は楽しいです。おすすめ。