2008-09-17 59 views
17

我已创建WCF服务并正在使用netMsmqBinding绑定。如何处理WCF的MSMQ绑定中的消息失败

这是一个简单的服务,它将Dto传递给我的服务方法,并且不期待响应。该消息被放置在MSMQ中,并且一旦被拾取,就插入到数据库中。

确保没有数据丢失的最佳方法是什么?

我已经试过2种以下方法:

  1. 抛出一个异常

    这将邮件放在手动审阅死信队列。当我开始strvice

  2. 集上的绑定

    3之后尝试的receiveRetryCount =“3”我可以处理这一点 - 这instantanously发生,这似乎留在队列中的消息,但故障我的服务。重新启动我的服务将重复此过程。

我非常希望做如下:

尝试过程中的消息

  • 如果失败,等待5分钟,该邮件,然后再试一次。
  • 如果该过程失败3次,请将消息移至死信队列。
  • 重新启动该服务会将所有来自死信队列的消息推回到队列中,以便可以对其进行处理。

我可以做到吗?如果是这样如何? 你能指点我一些关于如何最好地利用WCF和MSMQ给我的场景的好文章。

任何帮助将不胜感激。谢谢!

一些额外的信息

我使用MSMQ 3.0在Windows XP和Windows Server 2003 不幸的是我不能使用内置的针对MSMQ 4.0和Vista/2008带毒邮件支持。

回答

9

有没有在这可能是你的情况很有用SDK的样本。基本上,它所做的是将一个IErrorHandler实现附加到您的服务,当WCF将消息声明为“毒药”(即所有配置的重试已用尽时)将捕获该错误。该示例所做的是将消息移动到另一个队列,然后重新启动与该消息关联的ServiceHost(因为在发现毒害消息时它将发生故障)。

这不是一个很漂亮的样本,但它可能是有用的。但有一些限制:

1-如果您有多个端点与您的服务相关联(即通过多个队列暴露),则无法知道有毒邮件到达哪个队列。如果您只有一个单队列,这不会是一个问题。我还没有看到任何官方的解决方法,但我已经试验了一个可能的替代方案,我在这里已经记录:http://winterdom.com/weblog/2008/05/27/NetMSMQAndPoisonMessages.aspx

2-一旦问题消息移动到另一个队列,它就成为你的责任,所以一旦超时完成(或将新服务附加到该队列来处理它),它将由您决定将其移回到处理队列中。

说实话,在这两种情况下,你都在寻找一些“手动”的工作,WCF本身并没有包括它。

我最近一直在做一个不同的项目,我有一个要求明确控制重试发生的频率,而我目前的解决方案是创建一组重试队列并在重试队列和主体之间手动移动消息基于一组计时器和一些启发式处理队列,只需使用原始的System.Messaging内容来处理MSMQ队列。它似乎很好地工作,但如果你这样做,有几个陷阱。

14

我觉得跟MSMQ(缴费仅在Vista),你也许可以做到这样的:第一次呼叫失败后

<bindings> 
    <netMsmqBinding> 
     <binding name="PosionMessageHandling" 
      receiveRetryCount="3" 
      retryCycleDelay="00:05:00" 
      maxRetryCycles="3" 
      receiveErrorHandling="Move" /> 
    </netMsmqBinding> 
</bindings> 

WCF将立即重试ReceiveRetryCount倍。批次失败后,邮件将被移动到重试队列 。 RetryCycleDelay延迟一分钟后,消息从重试队列移至端点队列,并重试批次。这将会重复 MaxRetryCycle时间。如果所有失败的消息都是根据receiveErrorHandling来处理的,那么可以移动 (去毒队列),拒绝,丢弃或错误

顺便说一句关于WCF和MSMQ的好文本是Progammig WCF书的第9章朱瓦尔·洛

+0

这适用于MSMQ 4.0而不是MSMQ 3.0 – Perhentian 2011-11-16 12:34:19

1

可惜我卡在Windows XP和Windows Server 2003,这样是不是我的选择。 - (我将重新澄清,在我的问题,因为我发现后发现这个解决方案,并意识到我不能使用它)

我发现一个解决方案是设置一个自定义处理程序,将我的消息移动到另一个队列或毒队列并重新启动我的服务。 这对我来说似乎很疯狂。想象一下,我的Sql Server停止了服务重启的频率。

所以我已经结束了做是允许的线路故障和队列留言。 我也记录致命的消息到我的系统日志记录服务,这已发生。 一旦我们的问题得到解决,我重新启动服务,所有的消息开始得到处理。

我意识到重新处理此消息或任何其他都会失败,那么为什么需要移动此消息和其他人到另一个队列。我也可以停止我的服务,并在所有操作按预期进行时重新开始。

澳苷,你有完美的答案为MSMQ 4.0,但遗憾的是不适合我

4

如果您使用的SQL服务器,那么你应该使用分布式事务,因为两者的MSMQ和SQL-Server的支持它。会发生什么情况是将数据库写入TransactionScope块并仅在成功时调用scope.Complete()。如果失败,那么当你的WCF方法返回时,消息将被放回到队列中再次尝试。这里的代码修剪的版本我用:

[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=true)] 
    public void InsertRecord(RecordType record) 
    { 
     try 
     { 
      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) 
      { 
       SqlConnection InsertConnection = new SqlConnection(ConnectionString); 
       InsertConnection.Open(); 

       // Insert statements go here 

       InsertConnection.Close(); 

       // Vote to commit the transaction if there were no failures 
       scope.Complete(); 
      } 
     } 
     catch (Exception ex) 
     { 
      logger.WarnException(string.Format("Distributed transaction failure for {0}", 
       Transaction.Current.TransactionInformation.DistributedIdentifier.ToString()), 
       ex); 
     } 
    } 

我的排队大,但已知数量的记录测试,让WCF开始大量的线程同时处理很多的(达到16个线程 - 16个消息立即关闭队列),然后在操作过程中终止进程。当程序重新启动时,消息从队列中读回并进行再次处理,如同没有任何事情发生一样,并且在测试结束时数据库是一致的并且没有丢失记录。

分布式事务管理器具有环境存在,当您创建一个TransactionScope的新实例时,它会自动搜索方法调用范围内的当前事务 - 应该在WCF弹出消息离开队列并调用你的方法。

+0

嗨克里斯。我相信通过将您的操作行为归因于TransactionScopeRequired = true,您无需将Sql调用包装在事务处理范围中,因为这已经完成了。 这就是说,我不确定你的答案与我的MSMQ问题有何关系。 – WebDude 2008-09-17 13:23:30

相关问题