こんにちワン、サーバーワークスの楽しい仲間・川口です。WEBサービスにおいてメールを送信する機会は必ず訪れますよね?メールサーバーを立てたり外部サービスを利用したりと既存の方法は物理サーバーであってもEC2であっても特に変わりありません。しかし何処かの大天使曰く「人が持つ唯一絶対の力、それは自らの意志で進むべき道を選択することだ」というので今回はAmazon SESを選択・利用してみました。
Amzon SES(Simple Email Service)とは?
Amazonのインフラを利用した大規模メール配信システムであり、このサービスを利用することによって自社でEメールソリューションを用意・操作・保守する煩雑さを無くし、サードパーティーEメール配信サービスを用いるより低コストで運用することが可能になります。各種APIを直接叩くかAmazonが配布している各種SDKを利用できます。
詳細はこちら → Amazon SES
※今回は「AWS SDK for PHP」を利用しました。
quota(クォータ)とrate(レート)
24時間内において送信できるメール上限をquota(クォータ)、1秒間に送信できるメール送信数をrate(レート)と呼びます。
quota値のリセット
※検証中な為事実と異なる記述の可能性があります!
検証当初はquota値は24時間毎にリセットされると思っていたのですが、実際には一定時間毎にquota値は一定割合で減少していく仕様となっているようです(割合や間隔等は不明)
(予想)
10→20→30→40→50→60→(リセット時刻)→0
(実際)
10→20→30→40→50→60→(送信を止める)→51→44→27→13→0
といった具合に徐々に減少していく様子が確認されました。一定時間ごとに送信可能数が回復するため例え上限値へ達してもキューイング等で正常な制御さえ行えば常に24時間間隔での運用が必要ではなくなる為応用範囲が増えるのではないでしょうか? (「24時間以内に〜」ではなく「順次確認メールを配送します」として処理したり)
送信数はどうやって調整すれば良い?
現時点でのmax-quotaやmax-rateを確認しながら送信側による制御が必要なようです。リファレンスでも送信予定数と現在の送信数をmax-quotaと計算して徐々に配信していく形式を推奨しています。
どうやったらquotaは増える?
正しく利用していたら徐々に増えるよ(超意訳)!マニュアルによると送信数やBounce数、送信したコンテンツ内容から各種ブラックリストに載っていない事など複数の条件があるようですが詳細については秘密のようです。
実際に試したこと
- productionアカウント(デフォルト設定値である1,000件)
- 1日500件前後のメールを送信(時間を置いて複数回)
- 副社長による「サーバーワークス通信」(内容は毎回変えた)
- 期間は5日(マニュアルには3日後から〜とあったので余裕を持って)
この程度ではやっぱり増えない。 やはり配信制御を行いながらギリギリのラインで攻める必要がありそうです・・・
〇月〇日までに××××件は送れないと困る・・・
申請フォームによる申請が確実との回答をamazonの方にいただいています。
sandboxとproduction?
sandboxは所謂「テスト版」の事です。production申請を出さずに利用している場合は必ずsandbox版となります。機能としてはproductionと差異がありませんが下記のような特徴があります。
- 送信数が「200件/日」で固定
- メール本文の審査などは行われない
- 送信先メールアドレスを事前に認証しておかなければならない
sandbox時の送信については内容や送信数によるamazon側の審査がないのでテストメールによるアカウント停止などを受けないので安心してテスト配信が可能となります。
productionのとしての利用にはamazonへの申請が必要となりますがAWSの他サービスと同様に内容に問題が無ければ通常2-3営業日くらいで返信がくるかと思います。
SESを使ってみる
「AmazonSES」へのサインアップ →その他のサービス同様の手順なので省略 SDKのダウンロード・セットアップ
- 解凍後の[sdk-1.3.2/sdk-1.3.2]が本体
- AWS-KEYをセット
- [config-sample.inc.php]→[config.inc.php]に変更
- サインアップしたamazonアカウントのキーをセット
既にたくさんの人がブログなどに挙げているので細かいところは抜きにします。 とりあえず認証周りは省略して下記の2点のサンプルコードを記述します。
メール送信
(ses-send.php)
require_once 'sdk.class.php';
require_once 'services/ses.class.php';
$ses = new AmazonSES(AWS_KEY, AWS_SECRET_KEY);
$res = $ses->send_email('xxx@example.com',
array('ToAddresses' => array('vvvv@example.com')),
array(
'Subject' => array('Data' => "これはAWS-SES送信テストです",
'Charset' => 'ISO-2022-JP'),
'Body' => array('Text' => array(
'Data' => '関係の無い方が受信された場合は破棄してください',
'Charset' => 'ISO-2022-JP'))
),
array('ReturnPath' => 'xxx@example.com'));
echo $res->isOK() ? "OK": "NG";
===================
$ php ses-send.php
OK
SES情報取得
※レスポンスはxml形式なので下記のような簡単なwrapperを用意
(SESResponseWrapper.class.php)
require_once 'sdk.class.php';
require_once 'services/ses.class.php';
class SESResponseWrapper
{
public function __construct()
{
$ses = new AmazonSES(AWS_KEY, AWS_SECRET_KEY);
$this->response = $ses->get_send_quota();
$this->quota_results = $this->response->body->GetSendQuotaResult;
}
public function getSentLast24Hour()
{
return $this->quota_results->SentLast24Hours[0];
}
public function getMax24HourSend()
{
return $this->quota_results->Max24HourSend[0];
}
public function getMaxSendRate()
{
return $this->quota_results->MaxSendRate[0];
}
}
(ses-info.php)
require_once 'SESResponseWrapper.class.php';
$response = new SESResponseWrapper();
echo $response->getSentLast24Hour(); // 24時間以内に送信したメール
echo $response->getMax24HourSend(); // 1日辺りの送信上限数
echo $response->getMaxSendRate(); // 1秒間辺りの送信可能数
SES利用に注意が必要(だと感じた)ところとか
(1)送信元が配送毎に変更されるシステム
productionアカウントの場合でも送信'元'アドレスは事前認証が必須です。例えばユーザー毎に特定のグループへ自分名義でメルマガを配送するようなサービスやシステムの場合はAWSとユーザー間での認証が発生してしまうので考えものです。
(2)会員登録のアクティベートメール
サンキューメールやメルマガであれば多少の増減は予測が付きますが突発的なアクセス増はいつも予測がつくとは限りません。送信数に上限があるため、送信制御を行っているなら限度以上の申し込みがあった際には必ず待ちが発生してしまいます。いざという時の代替手段や繁忙時の送信遅延保証については事前に配慮が必要です。
おまけ
上限を超えた場合にどうなるの?
該当AWSアカウントのSES機能が停止されてしまいます。停止されると一切のSES経由でのメール配信が行われずエラーステータスが返るようになり、解除するにはサポートへ送信理由等の釈明をし、それが認められる必要があります。非常に焦りましたがGoogle翻訳による拙い英語でもなんとか解除してもらえました・・・(たしか5日程で停止解除メールを受け取ることができました)。
おまけ2
Rubyフレームワークsinatra製のSES送信状況をグラフで確認できるライブラリがあったのでご紹介させていただきます。現在の送信数・認証済メールアドレス、過去2週間分の送信数・バウンスメール等の遷移状態をグラフ化して表示してくれるので大変便利です。
実は上記のライブラリはグラフ作成にGoogleChartへのGETリクエストを発行するため2000文字を超えるリクエスト(つまり大量の送信履歴がある場合等)はエラーになってしまいます。POSTでGoogleChartへリクエストを投げるように改修したものをGithubにおいておきますのでグラフが上手く表示されない方は試してみてください。 (forkして改変したものはもう少し手を入れてからpull requestします)
git@github.com:tar-to-key/SESChange.git
まとめ
送信上限・間隔等については気をつける点がありますが大量のメールを配信するに当たりこうしたサービスを利用することによってコストや信頼性において多くのメリットを享受できます。弊社でも既にいくつかの案件での利用を開始していますが先に上げた上限さえクリアしてしまえば構築後のメンテナンスを不要とする点は開発者にとっても大きな魅力の一つとなります。