使用 Step Functions 无需轮询数据库即可安排电子邮件发送
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
发送电子邮件是网络应用程序的一项相当标准的功能。电子邮件服务提供商提供简单的 API,只需几行代码即可发送电子邮件。
但安排在未来某个时间发送这些提醒就麻烦多了。假设你想为新用户安排一个为期几天的引导流程,或者你需要发送用户在应用中创建的任务或事件的提醒。现在你必须:
ScheduledEmails在数据库中创建一个表,用于存储未来日期的消息。- 创建一个定时任务,轮询此表以查找待发送的电子邮件,并在邮件发送完毕后将其清除。
- 维护运行 cron 作业的服务器。
这明明是一件简单得多的事情,却要花这么多时间去刮牦牛毛。
但还有一种更优雅的方法……
通过使用 AWS Lambda和Step Functions,您可以获得以下解决方案:
- 不需要数据库
- 不涉及民意调查
- 您只需为实际使用量付费。
- 为您的应用程序提供一个简单的 API 调用来调用 Lambda 函数(该函数本身可以被视为一个微服务)。
如果你想直接查看完整代码库,请点击这里。
如果你只想了解关键点,请继续跟随我的讲解,我将逐步介绍如何使用 Node.js 和 Serverless Framework 来实现这个功能。
📖相关内容:为什么无服务器新手应该使用部署框架。
侧边栏:虽然我在这里使用了电子邮件,但这种方法可以应用于您的应用程序需要执行的任何未来计划任务。
准备工作
首先,安装 Serverless Framework。
我们还将使用serverless-step-functions插件,以便轻松地在serverless.yml文件中配置 Step Functions。
配置 Step Functions 序列
序列中共有 2 个状态:
WaitForDueDate:这只是一个等待步骤,它从输入对象中读取一个dueDate字段,并等待直到到达该时间,然后再继续到下一个状态。SendEmail:一个 Lambda 函数,用于调用 API 发送电子邮件。
配置如下:
# serverless.yml
# ...
stepFunctions:
stateMachines:
EmailSchedulingStateMachine:
name: EmailSchedulingStateMachine
definition:
Comment: "Schedules an email to be sent at a future date"
StartAt: WaitForDueDate
States:
WaitForDueDate:
Type: Wait
TimestampPath: "$.dueDate"
Next: SendEmail
SendEmail:
Type: Task
Resource: "arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:${self:service}-${opt:stage}-SendEmail"
End: true
创建 SendEmail 函数
以下代码定义了 Lambda 函数的处理程序SendEmail,该处理程序由 Step Functions 配置中的 SendEmail 状态引用。该函数使用AWS SES发送电子邮件。
const AWS = require('aws-sdk');
const ses = new AWS.SES();
const { EMAIL_SENDER_ADDRESS } = process.env;
module.exports.handle = async (event) => {
const result = await sendEmail(event.email);
console.log('Sent email successfully', result);
return result;
};
function sendEmail(email) {
const params = {
Destination: {
ToAddresses: email.to,
},
Message: {
Subject: {
Data: email.subject,
},
Body: {
Html: {
Data: email.htmlBody || email.textBody,
},
Text: {
Data: email.textBody || email.htmlBody,
},
},
},
Source: EMAIL_SENDER_ADDRESS,
};
return ses.sendEmail(params).promise();
}
启动调度
现在,发送邮件的逻辑已经实现并连接到 Step Functions 状态机,我们需要一种方法从您的应用程序中启动该 Step Functions。这可以通过使用startExecutionStep Functions API 中的函数来实现。
// schedule-email.js
const AWS = require('aws-sdk');
const stepfunctions = new AWS.StepFunctions();
module.exports.scheduleEmail = async (dueDate, email) => {
const stateMachineArn = process.env.STATEMACHINE_ARN;
const result = await stepfunctions.startExecution({
stateMachineArn,
input: JSON.stringify({
dueDate, email
}),
}).promise();
console.log(`State machine ${stateMachineArn} executed successfully`, result);
return result;
}
// ----------------------------------------------
// your-app-module.js
const scheduleEmail = require('./schedule-email');
const email = {
to: ['test@example.com'],
subject: 'MyApp Onboarding Day 1 - Setting up your project',
textBody: 'TEXT body.\nsecond line.',
htmlBody: '<strong>HTML</strong> body<br>second line.'
};
const dueDate = '2018-11-16T13:55:25.000Z';
const result = await scheduleEmail(dueDate, email);
// result :
// {
// "executionArn": "arn:aws:states:eu-west-1:123456789:execution:EmailSchedulingStateMachine:84b1f498-ab4d-4f69-a635-ab1fa5199e16",
// "startDate": "2018-11-16T16:19:23.560Z"
// }
您需要更新应用程序运行所使用的 IAM 角色,以允许states:StartExecution对EmailSchedulingStateMachine状态机执行此操作。
如果我需要取消已安排的电子邮件怎么办?
该startExecution调用返回一个对象,其中包含本次执行的唯一executionArn字符串。如果您需要取消电子邮件发送,可以存储此 ARN,然后stopExecution在执行进入 SendEmail 状态之前(假设执行尚未结束)将其传递给该函数以取消执行。
你自己试试看
如果您认为这对您有用,请克隆此仓库并自行部署。
您可以进行以下一些改进:
- 如果邮件发送失败,请添加重试逻辑(插件支持
serverless-step-functions)。 - 支持除 SES 以外的电子邮件提供商
💌如果您喜欢这篇文章,可以订阅我的每周简讯,了解在 AWS 上构建无服务器 Web 应用的技巧。
您可能还会喜欢:
原文发表于winterwindsoftware.com。
文章来源:https://dev.to/paulswail/schedule-emails-without-polling-a-database-using-step-functions-1b10
