Nservicebus 在发布/订阅模型中,如何基于某些外部状态进行订阅者暂停处理?

Nservicebus 在发布/订阅模型中,如何基于某些外部状态进行订阅者暂停处理?,nservicebus,Nservicebus,我的要求是让订户暂停处理消息,这取决于web服务是否启动。因此,当web服务关闭时,消息应该不断从发布服务器到达订户队列,并不断堆积,直到web服务再次启动。(这些消息不应转到错误队列,而应留在订户队列上。) 我试图使用unsubscribe,但发布者停止发送消息,因为unsubscribe似乎清除了RavenDB上的订阅信息。我还尝试在传输类上设置MaxConcurrencyLevel,如果我将工作线程设置为0,则到达订阅者的消息将直接进入错误队列。最后,我尝试了Defer,它似乎将当前消息放

我的要求是让订户暂停处理消息,这取决于web服务是否启动。因此,当web服务关闭时,消息应该不断从发布服务器到达订户队列,并不断堆积,直到web服务再次启动。(这些消息不应转到错误队列,而应留在订户队列上。)

我试图使用unsubscribe,但发布者停止发送消息,因为unsubscribe似乎清除了RavenDB上的订阅信息。我还尝试在传输类上设置MaxConcurrencyLevel,如果我将工作线程设置为0,则到达订阅者的消息将直接进入错误队列。最后,我尝试了Defer,它似乎将当前消息放在审核队列中,并创建消息的克隆,并在超时完成时将其本地发送到订户队列。此外,由于我必须不断检查服务状态并不断延迟,我无法控制消息的顺序,因为我无法预测web服务何时启动


实现我所解释的行为的最佳方式是什么?我使用的是NServiceBus版本4.5

听起来您想一直尝试处理消息,直到消息成功为止,而不是将其拖回队列中(保持在顶部并继续尝试)

我认为您唯一的纯NSB选项是修改MaxRetries设置,它控制第一级重试:。将MaxRetries设置为一个非常高的数字可能会满足您的需求,但我无法想象这样做会是一个好的实践

第二级重试将延迟消息一段可配置的时间,但IIRC将允许从主队列处理其他消息

我认为您最好的选择是将重试逻辑放入您自己的代码中。因此,在抛出异常和NSB的重试功能开始之前,处理程序可以尝试在一个循环中访问服务x多次(可能是延迟)

编辑:

您的要求似乎是:

当一个MyEnter进来时,我需要做一个WebService调用。如果WebService被关闭,我需要在Y区间上尝试X次,在这一点上,我会认为它是一个失败并处理一个失败的条件。直到我成功或失败,我将阻止其他消息被处理。 您在处理消息时有一些潜在的复杂逻辑(重试、超时、错误条件、阻止其他消息等)。请记住NSB打算在您的系统中扮演的角色:通过消息传递在服务之间进行通信。虽然NSB确实有一些允许消息编排的高级功能(如sagas),但它并不是真正用来取代域或应用程序逻辑的


总之,您可能需要编写自定义代码来处理特定场景。一个简单的解决方案是处理程序中有延迟的循环,但您可能需要创建一个更健壮的内存中收集/队列,在服务关闭时保存消息,并在服务恢复时串行处理消息。

实现所需行为的最简单方法如下:

定义检查服务是否可用的消息处理程序,如果不可用,则调用HandleCurrentMessageLater和执行实际消息处理的消息处理程序。然后指定消息处理程序顺序,以便首先执行检查服务可用性的处理程序

public interface ISomeCommand {}

public class ServiceAvailabilityChecker : IHandleMessages<ISomeCommand>{
   public IBus Bus { get; set; }    
   public void Handle(ISomeCommand message) {
      try {
         // check service
      }
      catch(SpecificException ex) {
         this.Bus.HandleCurrentMessageLater();
      }
   }
}    
public class ActualHandler : IHandleMessages<ISomeCommand>{
   public void Handle(ISomeCommand message) {
   }
}    
public class SomeCommandHandlerOrdering : ISpecifyMessageHandlerOrdering{
     public void SpecifyOrder(Order order){
          order.Specify(First<ServiceAvailabilityChecker>.Then<ActualHandler>());
     }
}
公共接口命令{}
公共类服务可用性检查:IHandleMessages{
公共IBus总线{get;set;}
公共无效句柄(命令消息){
试一试{
//支票服务
}
捕获(特殊异常){
this.Bus.HandleCurrentMessageLater();
}
}
}    
公共类实际句柄:IHandleMessages{
公共无效句柄(命令消息){
}
}    
公共类SomeCommandHandlerOrdering:isSpecifyMessageHandlerOrdering{
公共无效指定人(订单){
order.Specify(First.Then());
}
}
通过这种设计,您可以获得以下好处:

  • 您可以在调用实际业务代码之前检查可用性
  • 如果服务不可用,则将消息放回队列
  • 如果服务可用,并且您的业务代码被调用,但就在调用ActualHandler之前,服务变得不可用,您将获得第一级和第二级重试(以及管道中的可用性检查)

订阅者自己是否能够检查服务是否在线?是的,订阅者可以检查服务是否已启动。如果服务已关闭,您可以使用saga和超时。一旦与web服务交互的处理程序完成并返回一条成功消息,就完成这个故事。有意义吗?我确实尝试过使用带有超时的Saga,但要求不断检查服务是否启动并超时,我如何实现这一点?消息处理程序立即完成,它不会等待超时处理程序返回。希望我用这种方式清楚地表达问题?!您可以有一个初学者处理程序来检查web服务,然后在您想要的时间段内超时,然后在超时处理程序中发出下一个超时…感谢您的回复。我不想因为你提到的同样原因而增加重试次数。有一件事似乎确实有效,那就是让当前线程睡眠,我认为这也不是一个好的实践。