Qdmailでの効率のいい大量メール送信

2010/12/11 | メール

最近はメール送信ライブラリとして、Qdmail+Qdsmtpをよく使っている。非常によく出来たライブラリで、巷でもよく使われている。使いやすくて素晴らしいライブラリだ。

Qdmail – PHP::Mail Library , Quick and Detailed for Multibyte

Qdsmtpを使うと1度SMTPコネクションをはると、そのコネクションを使い回して複数のメールを送信することができる。またQdmailと組み合わせて使えるよう設計されているので、メールを大量に送信する際にはとても都合が良い。ここでこの両者を組み合わせて大量メール送信を実現する際に注意したことや遭遇した問題についてメモしておく。

まず、メール本文が一律の場合は、何も気にする必要がない。envelopeToの設定だけでしていればよいので、1度の送信で複数の宛先にメールを送信することが出来るからだ。

次に気にすべきパターンは、メール本文に名前などを差し込む場合だ(メールによって本文が異なる)。これについてもQdmailにはメール本文のテンプレート挿し込み機能があるので問題ないし、宛先ごとにその都度本文を生成し、ループ処理でメールを送信すればよい。「ループ処理するだけならmb_send_mailでもよいのでは」ということもあるが、mb_send_mailでは、メールを1通送信するたびにSMTPコネクションを張ることになる(細かいことはさておきだいたいそのように考えて差し支えない)ので、処理速度が違うのだ。うちで使っているサーバ(非常に遅いサーバ)の場合、メール送信処理自体は0.01秒とか0.05秒とかで完了するのだが、SMTPコネクションをはる部分に1秒程度かかる。100通のメールを送信する場合送信するためにかかる時間は以下のようになる。

  • Qdmailの場合
    接続時間1秒+1通あたりにかかる時間(0.01秒)×100=2秒
  • mb_send_mailの場合
    (接続時間1秒+1通あたりにかかる時間(0.01秒)) ×100=101秒

これだけ違うのだ。前者のほうがサーバに優しいのは言うまでもないし、プログラマにも優しい(実行時間が長くなるといろいろなことを気にしなくてはいけない)。しかし前者を採用するにあたって、ちょっと複雑なことをしようと思うと、多少工夫が必要になってくる。その工夫については別途メモすることにする。

迷惑メールと判定されないために

2010/12/06 | メール

現在、とあるメール配信システムを開発している。別にメール配信のためだけのシステムというわけではなく、CRM付随のツールのような感じ。他の機能ができつつあり、ようやくメール配信の部分にこぎつけたのだがちょっと問題が出た。テストとしてGmailに送信したら迷惑メールに判定されてしまったのだ。

で、原因を少しだけ探ってみた(本格的に探ったわけではない)。まずメール本文にはURLがあるのだが、個々の受信者を識別するための識別キーを付与してある。いかにもスパムメールでありそうなURLであり、これが主な原因になっている可能性が大きい。

そこでこのURLを外してみたところ、やっぱり受信トレイに格納された。さてさて、識別キーをつけたURLをつけないとこのメールに意味がなくなるのだがどうしたものか。ということでいろいろなパターンを試してみることにした。

  • 識別キーのついたURLがある場合
    迷惑メール
  • URLを一切無くした場合
    受信トレイ
  • 識別キーを外したURLがある場合
    迷惑メール(ドメインがダメなのかも・・・ずっと使ってなくて寝かしてあったドメインなのに)

ここまでで、「だめじゃん」とあきらめモードだったけど、ふと気づいたことがあって試してみた。

  • 識別キーなしでURLのプロトコルをhttpsにした場合
    受信トレイ
  • 識別キーありでURLのプロトコルをhttpsにした場合
    受信トレイ

ん~。httpsは大丈夫そうなのかもしれない。まぁ本来httpsで運用するシステムなので、それで問題ないのであればいいのだけれど。いろいろケアしてあげないといけない部分なので他のフリーメールでも試してみることにする。

Postfixのエラー

2010/09/02 | メール

Postfixのエラーが出ている。

“warning: do not list domain ドメイン名 in BOTH mydestination and virtual_alias_domains”

このエラーを出さないためにmydestinationをコメントアウトしたのだが・・・。調べてみるとコメントアウトするだけではなくて、以下の設定が必要っぽい。

mydestination =

=の後には何も記述しない設定。一応エラーは出なくなった。

Postfixで書式に基づいてメールを受信する

2010/08/29 | メール

Postfixの設定メモ。現在開発中のシステムにメールを配信する機能がある。このシステムでメールを送信する際に、中にはエラーとなって戻ってくるメールアドレスも含まれているかもしれない。この時、エラーとなるメールアドレスを識別する手法を考えていた。

最初に考えた方法はErrors-toやEnvelope-fromにエラー受信用のメールアドレス(例えばerror@example.com)を設定しておき、エラーとして戻ってきたメールの本文中に含まれるメールアドレスを抽出して、それを処理する方法だ。しかしこの方法だと、メールアドレスの抽出で一苦労しそう。例えばエラーとなるメールアドレス以外に、相手方メールサーバの管理者のメールアドレス等関係のないメールアドレスが含まれている可能性もある。

そこで考えた方法がErrors-toやEnvelope-fromに以下のようなメールアドレスを設定する方法だ。
error_1q2w3e4r@example.com
1q2w3e4rはメールの宛先ごとに一意となるように設定する。つまり宛先メールアドレスごとにErrors-toとEnvelope-fromに個別のメールアドレスを設定してしまうのだ。これで一意となるキーの部分(1q2w3e4r)と宛先メールアドレスをマッピングしたデータを持っていれば、エラーの宛先を確実に取得することができる。

メールを配信するたびに、エラー受信用のメールボックスを作るわけにはいかないので、何らかの設定をしなくてはいけない。一番簡単に考えられる方法はcatch-allだ。catch-allとして特定のメールアドレスを指定しておけば、とにかくすべてのメールを処理することができる。しかしこの場合だと、なんでもかんでも処理してしまうため(存在しないメールアドレスへのスパムメールとか)、処理に負担がかかる。

そこでメールアドレスに正規表現を施して、処理を分岐する手法だ。いろいろ調べてやっと実現する方法を見つけた。

まずPostfixで単独ドメインでとりあえずメールサーバとしては機能しているという前提にたち、main.cfに設定を加える。末尾に以下の行を追加した。

virtual_alias_domains = example.com
virtual_alias_maps = regexp:/etc/postfix/virtual

上記1行目はバーチャルドメインの設定をしているのだが、このとき、main.cfで既に設定されているmydestinationの設定と同じドメインが設定されているとまずいらしい。なのでmydestinationの行はコメントアウトしてみた。

次に上記2行目にある/etc/postfix/virtualというファイルに、以下の1行を追加した。

/bounce_(.*?)@example\.com/     bounce

上記は宛先メールアドレスとしてbounce_で始まり、@example.comで終わるメールアドレスすべてをシステム内のbounceというメールアドレスに送信するための設定だ。つまり何でもかんでも処理するのではなく、bounce_で始まるメールアドレスのみ処理するように設定している訳だ。これにより大半のスパムメールを除外することができる。

そして以下のコマンドを実行する。

postmap /etc/postfix/virtual

その後Postfixを再起動すればOKだ。

意外とこういうのって調べても見つからなかったりする。数時間も費やしてしまった。

メール配信は直接SMTPをたたく

2010/01/13 | メール

PHPでメールを大量に配信する方法を検討している。ウェブの管理画面から送信処理をするのだが、CLI版PHPで書かれたメール配信プログラムを、DSO版(モジュール版)PHPでキックし、メール配信プログラム内では、ループして送信するような処理はずっと以前から記述していた。とりあえず、それで事は足りていた。

しかしいろいろ調べる内に、もっと真面目にメール配信する手法を検討しようと言う気になった。それはマニュアルのmail関数の注意事項を読んでから。

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

メールの送信はmb_send_mail関数を使っているが、おそらく基本的な動作はmail関数と同じだろうから、これでは効率が悪いと言うことになるのだろう。より効率良くするためには「直接SMTPを話す」事になるとのことだ。個人的には「PHPで直接SMPTを話すなんて遅そう」と思っているのだが、そうではないらしい。今回使ってみようと思うのは、QdSmtpとQdmailの組み合わせ。

QdSmtp
Qdmail

スポックさんと言う人が書いているのだが、この方のメール配信系のプログラムにお世話になっている人も多いようだ。テクメモで以前書いたQdmailReceiverもこの方の成果物だ。

プログラムの記述方法は、実際に使用してからメモするとして、これらのプログラムを使うことで、ひとつのソケット接続で、ぽこぽことメールを送信できるようになる。PHPで大量のメールを送信する際に注意すべき事項(調べて出てきた重要そうな事項)をメモしておくことにする。

  • mail関数を使用せず、PHPで直接SMTPを話す。
  • 一定数送信したら、一定時間休む。
    sleepするような処理を入れる方がよいらしい(迷惑メール対策等で携帯宛などの場合連続して送信された大量メールは拒否られる可能性がある)。
  • BCCで、例えば10通ずつとかまとめて送る方法もあり(TOの指定が必要だけど)。
  • 基本的にループで処理するのだが、全てのプロセスを一つのプロセスで実行しようとせず、プロセスを分ける方法も考える(メモリリークとか)。

さてと、どうやって書こうかな。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net