なるべく確実にメールを送信する処理を書くには
メール送信処理は単純にコードを書くだけではダメっぽいのでどうすればいいのかという話
メールが送信に失敗する状況
何らかのエラーによって送信したメールが送信者に差し戻されることをバウンスという。
バウンスにはソフト / ハードの2種類がある。
ハードバウンス
永続的なエラーによるバウンスで、そのメールアドレスには基本的に届けることができない。
例えばDNSの参照に失敗する、メールアドレスが存在しない、メールアドレスの書式が不正、受信拒否されている、など。
ソフトバウンス
一時的なエラーによるバウンス。時間をおいて再送信すれば届くことが多い。
例えばメールボックスがいっぱい、メッセージサイズが大きすぎる、サーバがダウンしている、など。
メール送信のエラー処理の書き方
つまりなるべく確実にメールを送信したいのなら、バウンスがソフトバウンスなら時間を少しおいて送信処理をリトライするような処理を書けば良い。
ソフトバウンスならエラーコードが400系、ハードバウンスなら500系になるので、1回目の送信で400系のエラーなら1, 2分間隔を空けつつ3回ほどリトライ処理を行い、
500系のバウンスの場合はリトライしても送れない可能性が高いのでなぜ送信できなかったかを確認できるようにログに書き出すなどしておく。
以下Perlで Email::Sender
を使ってなるべく確実にメールを送信する処理を書いた例
use Encode qw( encode ); use Scalar::Util qw( blessed ); use Log::Minimal qw( critf ); use Email::Sender::Simple qw( sendmail ); use Email::Simple; use Email::Sender::Transport::SMTP (); my $transport = Email::Sender::Transport::SMTP->new( host => $host, port => 587, sasl_username => $username, sasl_password => $password, ); my $email = Email::Simple->create( header => [ To => $address, From => $sender_address, Subject => encode('MIME-Header-ISO_2022_JP', 'タイトル'), ], body => encode('ISO-2022-JP', '本文'), ); redundanted_sendmail($email, +{ transport => $transport }); sub redundanted_sendmail { my ($email, $options) = @_; my $retry_num = 2; for (0 .. $retry_num) { eval { sendmail($email, $options) }; if (my $e = $@) { die $e unless blessed($e) && $e->isa('Email::Sender::Failure'); if ( $e->code =~ /^4[0-9]{2}$/ ) { if ( $_ >= $retry_num ) { critf "Send mail failed. code: %d %s", $e->code, $e->message; die $e; } sleep 60; } elsif ( $e->code =~ /^5[0-9]{2}$/ ) { critf "Send mail failed. code: %d %s", $e->code, $e->message; die $e; } else { die $e; } } } }
しかし、エラーが返ってきても何度もメールを送信していると相手からBANされることがあったりするので、少量の送信ならば無料枠で済むSendGridの無料枠を使ったほうが良さそう。