wcf - 在 WCF 应用程序中设置 MSMQ 以确保可靠的邮件传递

标签 wcf msmq

所以我的任务是设置 MSMQ,这样如果我们的邮件服务器出现故障(这似乎经常发生),邮件就会在队列中结束,并在它们恢复时被传送。话虽如此,我不得不说,除了我在过去 24 小时内学到的知识外,我对此知之甚少,但我相信我知道的足以采取正确的方法,但我想问问社区中的某个人,因为有些困惑我的同事们在我们的 WCF 应用程序中给出了一些现有设置。

目前我们有一些服务使用 msmq 作为端点协议(protocol)。端点看起来像这样

<endpoint address="net.msmq://localhost/private/Publisher"
behaviorConfiguration="BatchBehaviour" 
binding="netMsmqBinding"
bindingConfiguration="MSMQNoSecurity"
contract="HumanArc.Compass.Shared.Publisher.Interfaces.Service.IPublisherSubscriber"
name="PublishSubscriber"/>

这当然允许客户端进行服务调用,如果由于某种原因服务未启动,它将确保在服务恢复时处理调用。我认为它不会做的是,如果您的服务方法中有类似以下内容的内容。

try
{
    smtp.Send(mail);
    return true;
}
catch (System.Net.Mail.SmtpFailedRecipientException ex)
{
     throw new Exception("User Credentials for sending the Email are Invalid",ex);
}
catch (System.Net.Mail.SmtpException smtpEx)
{
   throw new Exception(string.Format("Application encountered a problem send a mail message to {0} ", smtpHostName),smtpEx);
}

WCF 不会以某种方式重试并再次发送消息,我对这个假设是否正确?

我认为我们应该有类似下面的东西来代替上面对 smtp.send() 的调用。 (来自http://www.bowu.org/it/microsoft/net/email-asp-net-mvc-msmq-2.html)

  string queuePath = @".\private$\WebsiteEmails";
  MessageQueue msgQ;
  //if this queue doesn't exist we will create it
  if(!MessageQueue.Exists(queuePath))
       MessageQueue.Create(queuePath);
  msgQ = new MessageQueue(queuePath);
  msgQ.Formatter = new BinaryMessageFormatter();
  msgQ.Send(msg);

然后在服务启动的某个地方(我还不确定在哪里)我们设置了一个事件处理程序,它实际上会调用 SmtpClient 对象上的 send()。像这样

 msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(msgQ_ReceiveCompleted)

总而言之,我的第一个问题是哪种方式更好?创建一个使用 net:msmq 作为协议(protocol)的服务,或者只是更改电子邮件方法以将消息放入队列并为其设置处理程序?下一个问题,如果我关于更改调用 SmtpClient.Send() 的方法的假设是正确的,那么我应该在程序中的什么地方连接 ReceiveCompleted? Out WCF 服务托管在 Windows 服务中,这意味着实际上有一个对 ServiceBase.Run(servicesToRun) 的调用。有什么地方可以把它连接起来吗?我使用 WCF 的经验是使用更简单的 IIS 托管服务,所以我不是 100% 确定。

谢谢 - 我意识到这是一个很长的问题,但我一直在努力研究它并且有很多信息,但我似乎无法找到一个明确的解释来说明以一种方式做事与以另一种方式做事的好处。

最佳答案

您使用 msmq 解决下游依赖项(在本例中为您的 smtp 服务器)中的可用性的方法是有效的。但是,您应该首先了解有关 msmq 的几件事。

如果您在 msmq 中创建队列,则默认情况下它是非事务性的。在这种模式下,队列不会提供您需要的那种有保证的交付语义。因此,将您的队列创建为事务性的。

然后您可以告诉 WCF 您的服务操作将在收到要处理的消息时加入事务。您可以通过定义一个 behavior on your service operation 来做到这一点实现:

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SendEmail(Something mail)
{
    ....
    smtp.Send(mail);
}

TransactionScopeRequired 告诉 WCF 服务操作应该加入用于将消息从发送方传输到接收方的同一事务中。 TransactionAutoComplete 声明服务方法应该在操作成功完成后提交事务。因此,在回答您上面的查询时,服务操作失败导致事务回滚。

此时会发生什么取决于您的 service bindings configuration .

<netMsmqBinding>
  <binding name="netMsmqBinding_IMyServiceInterface" 
           exactlyOnce="true" 
           maxRetryCycles="3" 
           retryCycleDelay="00:01:00" 
           receiveErrorHandling="Move"> <-- this defines behavior after failure
    ...
  </binding>
</netMsmqBinding> 

当出于任何原因未提交事务时(例如,发生未处理的异常),WCF 会将消息回滚到队列中并每分钟重试处理一次,最多 3 次(由 maxRetryCycles< 定义retryCycleDelay)。

如果此时间后消息仍然无法处理,则 receiveErrorHandling属性告诉 WCF 接下来要做什么(上面的绑定(bind)指定消息被移动到系统毒消息队列)。

注意:exactlyOnce 告诉 WCF 我们需要事务,每条消息将按发送的顺序恰好传递一次。

所以您原来的方法实际上是正确的,您只需要正确配置您的服务即可实现您想要的行为。

关于wcf - 在 WCF 应用程序中设置 MSMQ 以确保可靠的邮件传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13630262/

相关文章:

.net - 如何加快 WCF 客户端的启动性能

c# - 服务类应该引用其他服务类还是只引用其他存储库类?

c# - MSMQ + C#,接收带有编码 byte[] 正文的消息在 Windows 7 和 Windows XP 上的行为不同

msmq - MassTransit 或 nServiceBus 是否支持基于 HTTP 传输的 MSMQ?

c# - 使用c#将消息发送到另一台计算机msmq的公共(public)队列

c# - 已处理死信队列中的 MSMQ 无效签名错误

c# - 尝试访问 wcf 服务时,契约(Contract)为 'IService1' 的 ServiceEndpoint 上的 CustomBinding 缺少 TransportBindingElement 错误

c# - WCF 与托管应用程序通信?

wcf - 不使用 IIS 的 WCF 服务上的证书

ssl - MSMQ 无法通过 HTTPS 发送