自动化AWS Lambda邮件发送实践

在等待会计自动化发票发送流程的同时,决定先自动化这一端能做的事情。俗话说,条条大路通罗马,选择了开发一个AWS Lambda函数,并订阅一个公共SNS主题来触发它。

步骤1:准备发送邮件的函数

开发一个简单的node.js函数来发送邮件是相对直接的。首先,需要安装两个模块:

npm install nodemailer npm install nodemailer-smtp-transport

然后,函数如下:

var transporter = nodemailer.createTransport(smtpTransport({ host: 'email-smtp.eu-west-1.amazonaws.com', port: 587, auth: { user: '{ACCESS KEY}', pass: '{SECRET KEY}' } })); var text = 'Hi, Invoice! Thanks!'; var mailOptions = { from: 'from@me.net', to: 'to@someone.net', bcc: 'me2@me.com', subject: 'Invoice', text: text }; transporter.sendMail(mailOptions, function(error, info){ if(error){ console.log(error); } else { console.log('Message sent'); } });

部署时遇到挑战,因为脚本有一些依赖。如果选择在线编辑并粘贴脚本,可能会得到这样的错误:

"errorMessage": "Cannot find module 'nodemailer'"

但是,部署一个包含依赖的完整包非常简单。只需将文件夹中的所有内容(不包括文件夹本身)打包成zip文件并上传即可。这种方法的缺点是,不能再在线编辑代码。所以即使是修复一个微不足道的打字错误,也需要重新打包和上传。

步骤2:安排流程

一种简单的方法是使用Powershell调用方法并安排任务来运行脚本:

Invoke-LMFunction -FunctionName automatedEmails -AccessKey accessKey -SecretKey secretKey -Region eu-west-1

但不希望依赖任何机器(本地或EC2实例)。否则,可以用C#写几行代码来完成同样的工作。使用Lambda的想法是避免维护,并让一切运行在由AWS维护的基础设施上。

不可靠的城镇时钟

不幸的是,AWS没有提供简单的方法来安排Lambda函数的调用。为了简单起见,决定使用不可靠的城镇时钟(UTC),它本质上是一个每15分钟发送“敲钟”消息的公共SNS主题。

因为只需要一封邮件,不在乎它是否跳过一两下,只要它在一天中至少敲响一次。

状态管理

当然,为了避免用邮件轰炸会计,必须维护一个状态,以便每个月只发送一封邮件。但Lambda函数必须是无状态的。一些替代方案是使用AWS S3或DynamoDB。因为只需要一个简单的整数值,决定存储在S3上的一个文本文件中。所以首先,下载日志文件并检查最后发送的邮件月份:

function downloadLog(next) { s3.getObject({ Bucket: bucketName, Key: fileName }, next); } function checkDate(response, next) { var currentDay = parseInt(event.Records[0].Sns.Message.day); var currentMonth = parseInt(event.Records[0].Sns.Message.month); var lastMailMonth = parseInt(response.Body.toString()); if(isNaN(lastMailMonth)) { lastMailMonth = currentMonth - 1; } if((currentDay == targetDayOfMonth) && (currentMonth > lastMailMonth)) { next(); } else { context.done(null, 'No action needed'); } }

整合在一起

所以把所有的东西放在一起,最终的代码是:

var bucketName = '{BUCKET_NAME}'; var fileName = 'mail-automation-last-sent-date.txt'; var targetDayOfMonth = 7; exports.handler = function(event, context) { var async = require('async'); var AWS = require('aws-sdk'); var s3 = new AWS.S3(); var nodemailer = require('nodemailer'); var smtpTransport = require('nodemailer-smtp-transport'); var currentMonth; async.waterfall([ function downloadLog(next) { s3.getObject({ Bucket: bucketName, Key: fileName }, next); }, function checkDate(response, next) { var message = JSON.parse(event.Records[0].Sns.Message); var currentDay = parseInt(message.day); currentMonth = parseInt(message.month); var lastMailMonth = parseInt(response.Body.toString()); if(isNaN(lastMailMonth)) { lastMailMonth = currentMonth - 1; } if((currentDay == targetDayOfMonth) && (currentMonth > lastMailMonth)) { next(); } else { context.done(null, 'No action needed'); } }, function sendMail(response, next) { var transporter = nodemailer.createTransport(smtpTransport({ host: 'email-smtp.eu-west-1.amazonaws.com', port: 587, auth: { user: '{ACCESS KEY}', pass: '{SECRET KEY}' } })); var text = 'Hi, Invoice! Thanks!'; var mailOptions = { from: 'from@me.net', to: 'to@someone.net', bcc: 'me2@me.com', subject: 'Invoice', text: text }; transporter.sendMail(mailOptions, function(error, info){ if(error){ console.log(error); } else { s3.putObject({ Bucket: bucketName, Key: fileName, Body: currentMonth.toString(), ContentType: 'text/plain' }, function(err, data) { console.log('Updated log'); context.done(null, 'Completed'); }); } }); } ], function (err) { if(err) { context.fail(err); } else { context.succeed('Success'); } }); };

让看看它是否会帮助自动获得发票!

更好的方法是检查邮件中的发票,并只在未收到时发送。此外,在初始邮件之后发送一些提醒也是不错的。但作为新决议是逐步、渐进地前进,将其称为版本1.0,并将剩余的任务留给以后的版本。

主要目标是实现这个任务,而不必担心基础设施。仍然不用担心,这仅仅是因为一个好人(即Eric Hammond)决定为其他人设置一个公共服务。

在研究中,遇到了一些参考资料,说同样的任务可以使用AWS Simple Workflow (SWF)来完成。以前没有使用过这项服务。它看起来很复杂,感觉需要经历一个陡峭的学习曲线。在版本2中,应该看看SWF,它将...

允许处理复杂的工作流

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485