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

Ken*_*enn 3 wcf msmq

所以我的任务是设置MSMQ,这样如果我们的邮件服务器出现故障(这似乎经常发生),消息就会在队列中结束,并在他们重新启动时发送.有了这个说我不得不说除了我在过去24小时里学到的东西之外我对此并不多了但是我相信我知道我采取了正确的方法,但我想问社区中的某个人,因为有一些混乱我的同事在我们的WCF应用程序中给出了一些现有的设置.

目前,我们有一些使用msmq作为端点协议的服务.端点看起来像这样

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

这当然允许客户端进行服务呼叫,如果由于某种原因服务未启动,它将确保当服务恢复时,将处理呼叫.我不认为它会做的是你在服务方法中有类似的东西.

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);
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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

 msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(msgQ_ReceiveCompleted)
Run Code Online (Sandbox Code Playgroud)

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

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

tom*_*ern 6

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

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

然后,您可以告诉WCF,当您收到要处理的消息时,您的服务操作将在事务中登记.您可以通过在服务操作实现定义行为来执行此操作:

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

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

此时发生的情况取决于您的服务绑定配置.

<netMsmqBinding>
  <binding name="netMsmqBinding_IMyServiceInterface" 
           exactlyOnce="true" 
           maxRetryCycles="3" 
           retryCycleDelay="00:01:00" 
           receiveErrorHandling="Move"> <-- this defines behavior after failure
    ...
  </binding>
</netMsmqBinding> 
Run Code Online (Sandbox Code Playgroud)

当由于某种原因而未提交事务(例如,发生未处理的异常)时,WCF将消息回滚到队列并每分钟重试处理一次,最多3次(由maxRetryCycles和定义retryCycleDelay).

如果在此时间之后消息仍然无法处理,则该receiveErrorHandling属性告诉WCF下一步该做什么(上述绑定指定将消息移动到系统毒药消息队列).

注意:exactlyOnce告诉WCF我们需要进行事务处理,每条消息将按照发送的顺序传递一次.

因此,您的原始方法实际上是正确的,您只需要正确配置您的服务以实现您想要的行为.