読者です 読者をやめる 読者になる 読者になる

【WordPress】外部SMTPを使ってメール送信した時のタイムアウトエラーの対処

wordpress php

今回はWordPressネタです。
担当サイトで用意されたサーバが、wp_mail()を使ってローカルのsendmailからメール送信出来ないという状態になり、仕方なく外部のSMTPでメール送信を行う事になりました。(この辺の話については後述します。)

外部SMTPからメールを送信するためのプラグイン(Cimy Swift SMTPとは)

WordPressからメールを送信する場合に、指定したSMTPサーバに直接メールを送信するためのプラグインです。WPから送信するメールでGmailを使いたい場合などには、大変便利なプラグインです。今回はこのプラグインを使うことにしました。

プラグインのページはこちら
http://wordpress.org/plugins/cimy-swift-smtp/

タイムアウトエラー

まず、wp_mail()関数は、その内部を見てみるとmail()関数を使って送信処理が行われています。
ループ内でwp_mail()関数を使っているような場合でも、ローカルのsendmail利用なら数秒で済んでしまうのですが、外部SMTPを利用する場合には、たかが100通程度でもPHPのデフォルト設定である30秒のタイムアウトがいとも簡単に発生します。

本来、ループ内でwp_mail()を使うのは非効率な処理です。

PHPマニュアル mail() 抜粋
http://php.net/manual/ja/function.mail.php

mail() 関数は、大量のメールをループ内で送信するには 向いていないことに注意しましょう。この関数は 1 通のメールを送信するたびに SMTP ソケットをいったん閉じて開きなおします。これは非効率的です。
大量のメールを送信する場合は、 » PEAR::Mail および » PEAR::Mail_Queue パッケージを参照ください。

このプラグインは、wp_mail()のアクションフックで、接続先の変更以外にもmail()を利用して送信するようになっていました。なので1件ずつ外部のSMTPに接続するので激遅なのです。
マニュアルにあるように、大量送信したい場合にはPEARを使うのが正攻法ですので、環境的にPEAR利用が許される人はそのようにしてください。
ただ、私のように政治的理由からPEARを使えない場合には、あえて1件ずつmail関数で送信する処理のまま、力業で解決する方法を以下に記述します。とても原始的な方法なので、あえて書くまでも無いレベルだと最初に言っておきます。。。

対処法(力業編)

タイムアウトの設定時間を引き伸ばす

最も簡単な解決法は、タイムアウト時間の設定を引き上げることです。
php.iniの値を直接変えてしまう手もありますが、同一サーバ上に複数サービスが乗っているとか、共有サーバでphp.iniを変更出来ない場合もあると思いますし、本当に無限ループな処理に陥った時に困るので、set_time_limit()を使います。

PHPマニュアル set_time_limit()
http://www.php.net/set_time_limit

引数には、タイムアウトを引き延ばすための秒数を指定するのですが、あるサイトで「3600以上を指定しても実際には3600に制限される」という記述を見ました。(試していないので真偽は不明です)
引数に0を指定すると無制限になるようですが、これは出来れば避けたいところです。
というわけで、もし1時間以上の処理が見込まれる場合には、1回の処理ですべてやろうとしないで、処理を分割する事を考えるべきです。

処理を分割する

大量にメール送信を行っている場合、ユーザーマスタからユーザーデータを取得して、1ユーザーごとに処理するループになっているだろうと想定されます。(メール本文の冒頭に「◯◯様」みたいな、ユーザー固有な文章があるばっかりにね・・・)
これを例えば、パラメータによってユーザー情報を取得するSQL部分を「ユーザーIDが奇数の人だけ取得する」、「偶数の人だけ取得する」といった具合に分岐させるだけで、簡単に処理を2分割出来ます。
バッチ起動なら、引数で偶数処理か奇数処理か渡せるようにしておいて、2つプロセスを起動すれば良いだけだし、(ほぼ無いと思いますが)画面からのリクエスト起動なら、Ajaxなりで実行させればよろしいかと思います。

ただし接続先によっては、大量に連続したメール送信をすると弾かれるとか、警告されるとか、激おこメールが飛んでくるとかあるようです。その場合にはおとなしく100件単位(が目安だそうです)で処理を分割し、なおかつ一定時間以上間隔を空けるなどするしかないと思います。

さいごに

外部SMTPを使わざるをえない状況になっている事がそもそも問題ですが、こちらについてはいまだ解決に至っておりません。この件は、インフラエンジニア(クライアント社内の人)の担当領域になっています。おそらく今後も、解決する気は無いと思われます。。。
同一サーバ上に複数WPが乗っていて、それぞれ独自ドメインを割り当てており、仮想化などされておりません。(最初はDNSの逆引きも出来ないような状態だったので、迷惑メールで弾かれているだけかな?とか思ったりもしましたが、そのほうがまだ良かったな… ^^;)

メールが送信されないのはアプリの問題じゃないですか?的な事を言われた事もあったので、なんだかなぁ・・・という気持ちでいっぱいです。(-_-;;