c# - 要保存到数据库的 Microsoft Azure 服务总线主题工作流
问题描述
问题
目前,我们的网站设置为,当采取需要发送电子邮件的操作时,我们的网站将调用 SMTP 服务器以尝试发送电子邮件。问题出在 SMTP 服务器出于某种原因出现故障时。我们不会以任何方式存储任何外发电子邮件,因此如果要发送的电子邮件失败,它会永远丢失(不是真的,因为它可以很容易地重新生成,但我们没有机制让我们知道它失败了,除了Azure Application Insights
)。虽然我们也让网站在出现异常时向开发人员发送电子邮件,但出于显而易见的原因,我们不会收到这些电子邮件。
目标
我们的目标是停止让我们的网站直接向电子邮件中继服务器发送电子邮件。相反,实施一个可以发送电子邮件并在出现问题时能够恢复的解决方案。
- 阻止网站发送电子邮件
- 能够从瞬态或附带问题/异常中恢复
- 尽可能多地记录有关电子邮件的活动(发送尝试/失败/等)
- 能够从潜在的瞬态或附带问题/异常中恢复活动日志
- 如有必要,能够重新触发要发送的电子邮件(可选)
解决方案
我读了一篇由3 部分组成的文章,听起来它可以解决这个问题,我目前正在开发它。
我正在使用Microsoft.Azure.ServiceBus Topics
构建一个流程并Subscriptions
管理从我们的网站发送电子邮件。我已经浏览了许多示例,并且已经成功地能够适当地SendAsync()
a Message
、ReceiveAsync()
aMessage
和CompleteAsync()
或AbandonAsync()
它。
旁注:我现在正在探索如何使用RetryPolicy,看看这是否有助于我将重试推迟到更长的时间,但我不确定我是否可以/应该使用它。
虽然到目前为止大部分流程都已构建,因此我可以了解底层基础架构,但我仍处于计划阶段,以确保我们进行适当的计划。
我们目前正在尝试为这个过程找出最佳或最合适的工作流程。我们认为Topics
需要两个:一个用于发送电子邮件EmailTopic,另一个用于记录LogTopic的日志。
原因LogTopic
是在尝试将日志活动保存到数据库时处理任何暂时性问题。例如:我成功检索到要发送的电子邮件。然后我尝试发送电子邮件并记录此尝试。电子邮件已成功发送。然后我尝试记录此活动,但数据库刚刚关闭,我将无法记录此活动。第二个Topic
应该可以缓解这种情况,但是如果这种情况下降会发生什么?
这是我们当前的工作流程:
- 网站将数据插入到定义要发送的电子邮件的数据库中(目前,我们将有一个字段作为
Body
电子邮件内容本身,另一个表Email Templates
将包含Body
字段周围的内容,以及来自,至、抄送、密送和文件附件) - 网站发送一个小
Message
到EmailTopic
带有MessageId
插入记录的 - A
Stateless Service Fabric Service
监听消息 - 接收
Message
,从数据库中获取所有详细信息以供记录 - 构建
SMTPClient
并尝试将电子邮件发送到 SMTP 服务器 - 发送一个
Message
到LogTopic
,MessageId
当前日期,当前DeliveryCount
,和采取的行动(尝试发送电子邮件)- 如果成功,
CompleteAsync()
则Message
发送一个Message
带有LogTopic
当前MessageId
日期、当前DeliveryCount
和采取的操作的邮件:“已发送电子邮件” - 如果不成功,
AbandonAsync()
然后Message
发送一个Message
到LogTopic
,MessageId
当前日期,当前DeliveryCount
,和采取的行动:“电子邮件发送失败”(10 次尝试后消息将自动放置在DeadLetterQueue
- 如果成功,
在此工作流程中,LogTopic
将包含所有已采取的操作,并将在收到消息时存储在数据库中。显然,如果消息因任何原因被放弃并发送DeadLetterQueue
,我们将有一个过程在稍后尝试插入它们。
问题
- 我们考虑在工作流中将日志存储到数据库中,但问题是“如果数据库同时出现故障怎么办?” (因此上周 Azure Central US 出现故障时)出现了,所以我们决定使用这个 2nd
Topic
。显然,如果出现Service Bus
故障,我们将无法发送此消息,而且我不知道如何从中恢复,除了记录 ETW 并以其他方式检查它们。我应该先尝试数据库保存,如果失败,发送Message
到Topic
? - 此服务中是否发生了太多事情,我应该拆分一些操作吗?
- 工作流程本身是否存在我们没有考虑到的缺陷或缺失项?
- 我们是否应该使用 1
Topic
并在消息中添加一个Label
,以便我们知道它是要发送的日志还是电子邮件?也许使用过滤器(不确定如何正确执行此操作,或者它是否适合此工作流程)? - 我们是否在这个 1 SO 帖子中问了太多问题,应该将每个问题分开?
解决方案
我认为可以通过跟踪和解决故障的方式改进工作流程。我正在提供实现这一目标的解决方案。
服务总线主题支持单个发送者的多个订阅者。这可以在订阅的帮助下实现。
您可以在一个主题下创建两个订阅,而不是将消息发送到两个主题。请参阅此处以使用规则将消息过滤到订阅中。
您可以为订阅创建不同的规则。将消息发送到主题后,消息的自定义属性将根据每个订阅的规则进行验证。根据验证结果,消息将进入具有所需规则条件的订阅。
假设创建的订阅是电子邮件和日志。无状态 Service Fabric 服务应侦听来自这两个订阅的消息。
网站应使用电子邮件订阅的自定义属性将消息发送到主题。
每当 Stateless Service Fabric 服务收到消息时,它应该启动一个线程来发送邮件。
成功发送电子邮件后,应向主题发送一条成功消息,其中包含日志订阅的自定义属性。如果发送邮件失败,电子邮件订阅中的消息应该是死信,并且失败消息应该发送到具有日志订阅的适当自定义属性的主题。
也侦听来自日志订阅的消息的无状态服务结构服务将创建一个线程,以便在消息到达时写入数据库。如果写入数据库失败,日志订阅中的消息应该是死信。
您可以监控两个订阅的死信消息计数,以确保没有失败。如果计数大于 0,则需要手动干预 Resubmitting the dead letter messages to the Topic。市场上可能有可用的工具来监视和重新提交死信消息,或者您也可以为此开发自定义应用程序。
我猜这个工作流程应该按预期工作。安装后,唯一需要注意的是两个订阅中的死信消息。
推荐阅读
- c# - MailKit.Net.Smtp.SmtpClient.AuthenticateAsync 错误
- java - 以编程方式检查 redshift 集群状态
- ios - 如何编辑领域对象并保存编辑?
- android - 使用 Android Management API 启用蓝牙
- ddev - 安装 ddev 后找不到“mkcert”命令
- android - 活动之间的幻灯片动画
- amazon-web-services - Azure DevOps AWS 无服务器应用程序部署 - 每个环境的 S3 存储桶
- php - 解析 RSS 提要 URL 在浏览器和 CURL 中不返回任何结果,但在 RSS 提要阅读器中返回正确的结果
- scala - 如何使用 spark 结构化流管理从 kafka 读取的偏移量
- makefile - 如何防止 make 过早运行 $(shell 命令)