Microsoft Azure 服务总线主题 保存到数据库的工作流

Microsoft Azure Service Bus Topics Workflow to Save to a Database

问题

目前,我们的网站已设置为当执行需要发送电子邮件的操作时,我们的网站将调用 SMTP 服务器以尝试发送电子邮件。问题出在 SMTP 服务器出于某种原因出现故障时。我们不以任何方式存储任何外发电子邮件,因此如果要发送的电子邮件失败,它将永远丢失(不是真的,因为它可以很容易地重新生成,但我们没有机制让我们知道它失败了,除了Azure Application Insights)。虽然我们也让网站在发生异常时向开发人员发送电子邮件,但出于显而易见的原因,我们不会收到这些电子邮件。

目标

我们的目标是停止让我们的网站直接向电子邮件中继服务器发送电子邮件。相反,实施一个可以发送电子邮件并能够在出现问题时恢复的解决方案。

  1. 阻止网站发送电子邮件
  2. 能够从瞬态或侧面恢复 issues/exceptions
  3. 尽可能多地记录有关电子邮件的内容 activity(发送 attempts/fails/etc)
  4. 能够从潜在的瞬态或侧面 issues/exceptions
  5. 恢复 activity 日志
  6. 必要时能够重新触发要发送的电子邮件(可选)

解决方案

我读到 3-part article 听起来可以解决这个问题,我目前正在开发它。

我正在使用 Microsoft.Azure.ServiceBus TopicsSubscriptions 构建一个流程来管理从我们的网站发送电子邮件。我经历了很多样本​​,并成功地 SendAsync() 一个 MessageReceiveAsync() 一个 MessageCompleteAsync()AbandonAsync() 它适当地。

旁注: 我现在正在探索如何使用 RetryPolicy 看看这是否会帮助我将重试推迟到更长的时间期间,虽然我不确定我 can/should 是否将它用于此目的。

虽然到目前为止已经构建了大部分流程,因此我可以了解底层基础架构,但我仍处于规划阶段以确保我们进行适当的规划。

我们目前正在尝试找出最适合此流程的工作流程。我们认为需要两个 Topics:一个用于发送电子邮件 EmailTopic,另一个用于记录日志 LogTopic

LogTopic 的原因是在尝试将日志 activity 保存到数据库时处理任何暂时性问题。 例如:我成功检索到要发送的电子邮件。然后我尝试发送电子邮件并记录这次尝试。邮件发送成功。然后我尝试记录这个 activity,但是数据库刚刚关闭,我将无法记录这个 activity。第二个 Topic 应该可以缓解这种情况,但如果这种情况下降会怎样?

这是我们当前的工作流程:

  1. 网站将数据插入定义要发送的电子邮件的数据库(目前,我们将有一个 Body 字段,它将是电子邮件内容本身,另一个 table 用于保存Email Templates 将包含 Body 字段周围的内容,以及发件人、收件人、抄送、密件抄送和文件附件)
  2. 网站用插入记录MessageId发送一个小MessageEmailTopic
  3. A Stateless Service Fabric Service 监听消息
  4. 收到 Message,从数据库中获取所有详细信息以供记录
  5. 构建 SMTPClient 并尝试将电子邮件发送到 SMTP 服务器
  6. LogTopic 发送 Message,其中包含 MessageId、当前日期、当前 DeliveryCount 和采取的操作(尝试发送电子邮件)
    • 如果成功,CompleteAsync() Message 并发送 MessageLogTopic MessageId,当前日期,当前 DeliveryCount,并采取行动:"email sent"
    • 如果不成功,AbandonAsync() Message 并发送 MessageLogTopic MessageId,当前日期,当前 DeliveryCount,并采取行动:"email failed to send"(尝试 10 次后消息将自动放置在 DeadLetterQueue

在此工作流程中,LogTopic 将包含所有已采取的操作,并在收到消息时存储在数据库中。显然,如果消息因任何原因被放弃并发送 DeadLetterQueue,我们将有一个过程在稍后尝试插入它们。

问题

  1. 我们考虑过只在工作流中将日志存储到数据库中,但是问题 "what if the db goes down in the meantime?"(因此当 Azure Central US 上周出现故障时)出现了,所以我们决定使用第二个 Topic.显然,如果 Service Bus 关闭,我们无法发送此消息,我不知道如何从中恢复,除了记录 ETW 并以其他方式检查它们。我是否应该先尝试数据库保存,如果失败,请将 Message 发送到 Topic?
  2. 这项服务中发生的事情太多了吗?我应该拆分一些操作吗?
  3. 工作流本身是否存在我们没有考虑到的缺陷或缺失项?
  4. 我们是否应该使用 1 Topic 并在消息中添加一个 Label 以便我们知道要发送的是日志还是电子邮件?也许使用过滤器(不确定如何正确执行此操作或者它是否适合此工作流程)?
  5. 我们是否在这个 1 SO post 中问了太多问题,应该将每个问题分开?

我认为可以通过跟踪和解决故障的方式改进工作流程。我正在提供实现此目标的解决方案。

服务总线主题支持单个发送者的多个订阅者。这可以在订阅的帮助下实现。

您可以在一个主题下创建两个订阅,而不是将消息发送到两个主题。请参阅 here 使用规则将消息过滤到订阅中。

您可以为订阅创建不同的规则。将消息发送到主题后,消息的自定义属性将根据每个订阅的规则进行验证。根据验证结果,消息将进入具有所需规则条件的订阅。

假设创建的订阅是电子邮件和日志。 Stateless Service Fabric 服务应侦听来自这两个订阅的消息。

网站应使用电子邮件订阅的自定义属性将消息发送到主题。

每当 Stateless Service Fabric 服务收到消息时,它应该启动一个线程来发送邮件。

成功发送电子邮件后,应将成功消息发送到主题,其中包含日志订阅的自定义属性。如果发送邮件失败,电子邮件订阅中的消息应该是死信,并且应该将失败消息发送到具有适当的日志订阅自定义属性的主题。

同时侦听来自日志订阅的消息的无状态 Service Fabric 服务将创建一个线程以在消息到达时写入数据库。如果写入数据库失败,日志订阅中的消息应该是死信。

您可以监控两个订阅的死信消息计数,以确保没有失败。如果计数大于 0,则应手动干预 Resubmitting the dead letter messages to the Topic。市场上可能有可用的工具来监控和重新提交死信消息,或者您也可以为此开发自定义应用程序。

我想这个工作流程应该会按预期工作。安装后,唯一需要注意的是两个订阅中的死信消息。