为SES配置SNS Event追踪每一封发送的状态

一、背景

AWS Simple Email Service是用于大规模发送推广邮件的服务,用于支撑会员系统等合理合法的邮件群发。SES的最佳实践是:每个邮件地址调用1次SES接口,单独发送一个唯一的地址。参考如下介绍。

发送限制基于的是收件人而不是电子邮件。例如,一封包含 10 个收件人的电子邮件占用 10 份配额。但是,建议不要在一次 SendEmail 调用中向多个收件人发送电子邮件,因为如果对 Amazon SES 的调用失败(例如,请求格式错误),整个电子邮件都将被拒绝,没有任何收件人能收到预期的电子邮件。我们建议您为每个收件人调用一次 SendEmail

https://docs.aws.amazon.com/zh_cn/ses/latest/DeveloperGuide/manage-sending-limits.html

SES默认是只显示管理整个domain的状态,包括邮件总数、退信比例等。如果希望追踪单封邮件发送,可以使用如下办法。

二、设置SNS主题并订阅

首先设置一个SNS主题。进入SNS服务,添加一个新主题,主题名字自选。

添加主题完成后,再其下添加订阅。点击添加订阅。

在添加订阅中,我们选择订阅的协议。本文档为了方便演示,且更加直观,将选择EMAIL-JSON格式作为订阅发送状态的渠道。这意味着,SES发送一封邮件后,将向订阅者的邮箱中发送一个JSON文件,其中包含当前这份邮件是否发送成功的状态。

如果是用于产品和项目,建议不要订阅到邮件,而是订阅到SQS队列,或者订阅到Lambda,使用程序代码继续处理后续的发送状态。

输入订阅者的邮箱后,如果此邮箱是第一次作为订阅者,SNS会给此邮箱发送一封确认邮件,邮箱的所有人需要点击这份邮件里边的连接方可成功的订阅。

订阅完成。

三、设置配置集

在SES中找到配置集选项。点击新建配置集。

进入配置集后,添加新的事件,可以看到支持三种:Cloudwatch、Kinesis、SNS。Cloudwatch的作用是生成报表,Kinesis可以流式的采集数据;SNS即发送到标志通知服务,然后由SNS再根据订阅者决定信息发送目标。

接下来选择要订阅的通知类型,包括发送成功、失败、退信等。在下方选择SNS的topic。请同时订阅第一行的这几种状态。

其中,所有邮件都会先经历第一个状态:Send。如果是成功,第二个状态将收到delivery。如果退信,将收到Reject或者Bounce。

配置完成。

四、发送邮件测试

为了发起测试,我们可以使用SES内置的邮箱模拟器,点击这里访问使用文档

如上截图,当需要测试不同的事件的时候,就可以把目标地址写为邮件模拟器提供的这几个测试地址。然后系统会根据刚才的设置,生成SNS通知,给出发送信息的反馈。

首先我们来测试发送成功场景,收件人地址使用:success@simulator.amazonses.com 。进入SES控制台,选中域名,选择发送邮件。选择使用RAW格式。然后在其中手工加入一行:

X-SES-CONFIGURATION-SET: ConfigSet

这行配置的作用是,在发送时候指定配置集,请把它替换为刚才创建配置集时候的名字。如下截图。

点击发送后,我们去看发送成果。可以看到SES分别将发送和投递成功两个Event返回给了SNS。SNS又将这两个Event的以订阅为Email-JSON的方式发送到邮箱。检查订阅邮箱,里边可以看到两封邮件。第一封是send(发送),第二封是成功(deilivery)。

接下来我们再验证一下退信的场景。

再次启动发件器,目标地址改为邮箱模拟器上的退信地址:bounce@simulator.amazonses.com ,然后执行发送。

现在查看SNS订阅邮箱,发现里边还会获得两个通知,第一个通知是发送(sent),第二个通知是被退信,且退信理由是SMTP服务器返回550代码,用户邮箱不存在。(当然这是AWS SES的邮箱模拟器模拟出来的退信效果)

由此,就实现了跟踪每一封邮件状态。

四、小结

以上方法展示了SES如何通过Event事件的订阅机制,跟踪每一封邮件。以上环节仅是Demo,如果用于生产环境,还会有一些差异。

首先发送邮件的方式是代码调用API执行,而非本文这样通过界面触发test email。代码的开发可参考这里的地址有完整sample代码。

第二是大规模发送邮件,要核实发送状态,不太可能通过email方式接受日志来判断投递状态。可能采用的方案是:

(1)从SES导出日志到SNS,使用Lambda订阅SNS,即用Lambda代码接受刚才Email里边的那段JSON。然后通过lambda将发送成功和失败的地址和状态(元数据)写入到DynamoDB,以方便后期调月。DynamoDB上打开生命周期TTL,自动清除大于30天的发送记录。

(2)从SES导出日志到Kinesis,然后使用Kinesis filehose将日志写入S3,再调用Athena运行SQL脚本查询日志,并完成后续逻辑。

以上过程供开发过程中的架构设计参考,如何实现看发送的规模和系统开销,并无绝对的标准答案,有兴趣的同学可以尝试引入更多AWS产品做更快更好的分析。