LoginSignup
59
61

More than 5 years have passed since last update.

ちょっと時間かかるけど Amazon SES でのメール送信エラーをなるべく検出する方法

Last updated at Posted at 2012-12-26

Amazon SES を使ってメールを送ると、普通は送信エラーをその場で検出できません。その場でっていうのは、非同期ではなくって意味です。ここでは SES に Amazon SNSAmazon SQS を組み合わせて、なるべく送信エラーをその場で検出するコードを紹介します。エラーチェックのためにちょっと時間かかりますが、我慢してください。

処理の概要としてはこんな感じです。

俺コード → 下にある SQS のキューをチェック
   ↓ メール送信
Amazon SES
   ↓ 送信エラー等のフィードバック
Amazon SNS
   ↓ フィードバックのキューイング
Amazon SQS

なんだか分かりにくいですね。エラーがあるときと無さそうなときに分けるとこうなります。

エラーがあるとき
SES でメール送信
 ↓
(AWS 内で SES から SNS へ送信エラーがフィードバックされる)
 ↓
(AWS 内で SNS から SQS へフィードバックがキューイングされる)
 ↓
SQS のキューをチェック
 ↓
送信エラーが発覚!
エラーが無さそうなとき
SES でメール送信
 ↓
SQS のキューをチェック...
 ↓
SQS のキューをチェック...
 ↓
SQS のキューをチェック...
 ↓
フィードバックが無いからきっと成功したんだ
(もしかしたらまだエラーになっていないだけかもしれない)

SES, SNS, SQS のつなぎ込みの詳細については公式ドキュメントに譲ります(SES → SNS, SNS → SQS)。それぞれのサービスに対する設定はざっくりこんな感じです。途中で色々な ARN を参照する必要が出てくるので、サービスごとにタブを開いておくと捗ります。

  • SES で送信元アドレスを作って、フィードバック設定に SNS のトピックを設定する
  • SNS でトピックを作り(名前は例えば ses-feedback)、サブスクリプションにキューを設定する
  • SQS でキューを作り、SNS のトピックに対して SendMessage パーミッションを設定する

この設定ができたら、送信エラー等のフィードバックが SQS に入ってくるか確認しましょう。コンソールで SES を開くなどして bounce@simulator.amazonses.com にメールしてください。このアドレスは SES の sandbox に書いてあるサンドボックス用アドレスです。サンドボックス外の人は zan-nen@sayaka.chan とかに送ってみても OK です。

メールを送信したら SQS のコンソールを開いてください。念のため右上の Refresh を押して Messages Available が 1 になってたらめでたく成功です。中身はたぶんこんな感じです。

{
  "Type" : "Notification",
  "MessageId" : "212f5821-b209-47bc-84c8-8cbed9de7c69",
  "TopicArn" : "arn:aws:sns:us-east-1:略:ses-feedback",
  "Message" : "JSONをエスケープしたもの",
  "Timestamp" : "2012-10-24T12:18:31.531Z",
  "SignatureVersion" : "1",
  "Signature" : "略",
  "SigningCertURL" : "略",
  "UnsubscribeURL" : "略"
}

ふぅ……ここまでで環境設定は終わり。ここからはコーディングです。言語は Python、ライブラリは boto を使っています。Java でも aws-sdk 使えば同じ処理を書くことができます。

# -*- coding: utf8 -*-

import boto
from boto.sqs.message import RawMessage
import json
import time

AWS_KEY = "YOUR_AWS_KEY"
AWS_SECRET_KEY = "YOUR_AWS_SECRET_KEY"

def send_email(source, subject, body, to_addresses, timeout=5):
    # SES、SQS へのコネクションを作成する。
    ses = boto.connect_ses(AWS_KEY, AWS_SECRET_KEY)
    sqs = boto.connect_sqs(AWS_KEY, AWS_SECRET_KEY)

    # キューの取得。RawMessage を指定しなかったらえらいことになった
    queue = sqs.get_queue("ses-feedback")
    queue.set_message_class(RawMessage)

    # ここまで下準備。ほんとはこの関数の外でやったほうがいい。

    # メールを送信する
    ses_resp = ses.send_email(source, subject, body, to_addresses)

    # メール送信結果の messageId を記録しておく。
    # キューの中を検索するときのキーとして使える唯一無二の文字列。
    ses_message_id = ses_resp["SendEmailResponse"]["SendEmailResult"]["MessageId"]
    print "Sent mail %s" % ses_message_id

    # タイムアウトするまでキューを調べる
    start_time = time.time()
    while time.time() < start_time + timeout:
        time.sleep(0.5)

        # キューに入っているメッセージを最大10個取得する。
        # キューから取ったメッセージはなるべく早く手放したいので visibility timeout は1秒。
        # 注: SQS の特性上、メッセージが10個入っていても一度に10個取れない場合がある
        sqs_messages = queue.get_messages(10, visibility_timeout=1)

        for sqs_message in sqs_messages:
            # メッセージの中からフィードバックそのものを取り出す
            ses_feedback = json.loads(json.loads(sqs_message.get_body())["Message"])

            # フィードバックがあったメール送信結果の messageId を出力
            print "Found feedback %s" % ses_feedback["mail"]["messageId"]

            # さっき送ったメール送信結果の messageId と一致すればキューから削除しておしまい。
            # 見つからなければタイムアウトまでキューをチェックし続ける。
            if ses_message_id == ses_feedback["mail"]["messageId"]:
                queue.delete_message(sqs_message)
                return False
    return True

if __name__ == "__main__":
    source = "sender として verify したアドレス"
    # 実行例
    print "should be true..."
    print send_email(source, "subject", "body", ["success@simulator.amazonses.com"])
    print "should be false..."
    print send_email(source, "subject", "body", ["bounce@simulator.amazonses.com"])

処理の説明は、コメントに書いたとおりです。正常にメールを送ることができた場合は、タイムアウトするまで待つことになります。タイムアウトの数字は各自のさじ加減でお願いします。個人的には長くて5秒、なるべく3秒かなぁと思ってます。実際にコードを走らせてみて、送信エラーの検出率を見ながら調整するのがベストですね。

サンプルコードの注意点とか:

  • JSON の要素チェックとかザルです。突然の死、あります。
  • SES からのフィードバックである bounce(バウンスメール)や complaint(苦情メール)以外がキューに入るケースを考慮していません。SQS への SendMessage を許可してないので入らないと思いますけど。
  • お金はほとんどかからないと思います。
  • 2012/10 現在、SES のフィードバックは US-East-1 の SNS にしか送れないようなので、日本リージョンへのこだわりがある方は残念でした。

参考にしたサイト:

59
61
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
61