NodeJS 在不超过费率限制的情况下,尽可能快地通过SES发送电子邮件

xkrw2x1b  于 2023-04-05  发布在  Node.js
关注(0)|答案(6)|浏览(106)

我目前正在使用SendGrid从许多无服务器的node.js Lambda函数发送电子邮件,并计划从SendGrid转移到SES(因为价格合理)。
SendGrid的速率限制非常高,以至于通过一些lambda函数并发地使用for循环向用户发送电子邮件从未引起问题。但是SES的速率限制较低(在我的情况下为每秒50封电子邮件),并且发生了最大发送速率限制超过错误。
如何在不超过速率限制的情况下尽可能快地发送电子邮件?
我目前的计划是通过多个lambda函数向SQS队列发送邮件请求,通过1个Lambda函数不定时运行接收SQS消息发送邮件,但我不知道如何控制向SES发送请求的速度。

sycxhyv7

sycxhyv71#

我的结论

我决定创建两个SQS队列(A和B)。队列B用于触发电子邮件发送功能,队列A用于在等待队列中添加消息。因此我只需要控制消息从队列A移动到B的速度,这使得一切都变得简单。

我考虑了以下几点。

错误处理

一开始,我想用while-loops通过一个Lambda函数发送多封邮件,但这样会给错误处理带来困难,当超过速率限制错误或其他意外异常发生时,需要等待一段时间重试,但可能会超过Lambda函数的超时时间。

可扩展性

通过单个Lambda函数在1秒内从SQS队列接收50条消息并向SES发送50个请求是可能的,但是如果速率限制上升到300或500,则可能不可能在一秒内处理那么多请求。将消息从一个SQS队列移动到另一个SQS队列,以便为每个电子邮件调用Lambda函数,相对容易扩展。

邮件优先级

注册确认电子邮件与验证码需要立即发送,但活动电子邮件可以发送延迟。有两个SQS队列,我可以控制优先级。

AWS成本

为每封电子邮件调用一个Lambda函数比在一个Lambda函数中发送多封电子邮件需要更多的时间,但这方面的AWS成本并不是很昂贵。如果向SES发送请求需要1秒,则在128 MB内存(北弗吉尼亚州地区)下执行Lambda的成本为0.000002083美元。对于1000封电子邮件,这仅为0.02083美元,可以忽略。

q3aa0525

q3aa05252#

您还可以使用Lambda Reserved Concurrency feature将并发Lambda执行的数量限制为50。并将SQS的批处理大小设置为1条消息。
在无服务器框架中,这看起来像:

sendNotificationsEmails:
    handler: lib/send-email/handler.handler
    reservedConcurrency: 50
    events:
      - sqs:
          arn: xxxxx
          batchSize: 1

这将确保您不会同时运行超过50个Lambda示例。
但是一个新的问题出现了-如果你的函数在〈1 s的时间内执行得太快,你最终会超过速率限制。

  • 重试请求直到成功
  • 按比例减少reservedConcurrency

如果你决定减少reservedConcurrency,假设你的代码在500 ms内运行。这意味着,如果你有50个并发的Lambda示例,它们将在前0.5s内发送50封电子邮件,然后在第二个0.5s内尝试发送50封电子邮件。
然后将reservedConcurrency设置为25

zwghvu4y

zwghvu4y3#

下面的方法保证当arrayWithAllTheEmails包含x或更多元素时,您将有x个并发调用。
每次一个呼叫结束时,它都会调用下一个呼叫。

let ongoing = 0

function sendXEmailsAtATime (arrayWithAllTheEmails, max = 50) {
  while (ongoing < max) {
    ongoing++
    const params = prepareParams(arrayWithAllTheEmails.shift())

    ses.sendEmail(params, (err, data) => {
      if (err) console.log(err, err.stack); // an error occurred
      else     console.log(data);           // successful response

      ongoing--
      if (arrayWithAllTheEmails.length > 0) {
        sendXEmailsAtATime(arrayWithAllTheEmails, max)
      }
    });
  }
}

function prepareParams (oneItemFromTheArrayWithAllEmails) {
  // ... here you prepare the params and return it
}
olhwl3o2

olhwl3o24#

你可以使用sleep()来实现这一点。

async function init() {
      console.log(1);
      await sleep(10000);
    }
q7solyqu

q7solyqu5#

溶液1

我认为你使用CloudWatch创建了一个预定事件,每隔1秒或2秒触发一次事件,它将调用lambda函数。在lambda函数内部,只从SQS中检索进程50条消息,并结束lambda函数。

溶液2

但我看到你把这个running without stopping time
如果您想这样做,那么您必须设置一个时间计数器,从开始向SES发送电子邮件请求到发送50个请求的时刻。
如果时间差大于1秒,则处理下一组电子邮件。
如果时间差小于1秒,则等待剩余时间,然后处理下一组电子邮件。
SES文档在此链接中讨论了相同的问题,并建议在代码中引入速率限制逻辑。
https://aws.amazon.com/blogs/messaging-and-targeting/how-to-handle-a-throttling-maximum-sending-rate-exceeded-error/
因为你是用node的所以你可以看看这个速率限制器库https://www.npmjs.com/package/ratelimiter

ctehm74n

ctehm74n6#

2023年来这里的每个人:

Amazon在使用Amazon SQS作为事件源功能时引入了AWS Lambda函数的最大并发性,这似乎是此任务的一个很好的用例。source
下面是我们如何实现节流:https://github.com/michaelhaar/Heimdall-Burstable-SES

相关问题