C# Rebus中某一消息类型的串行处理
我们有一个与第三方Web服务对话的REBS消息处理程序。由于我们无法直接控制的原因,此WCF服务经常引发异常,因为它在自己的数据库中遇到数据库死锁。然后,Rebus将尝试处理此消息五次,这在大多数情况下意味着这五次中的一次是幸运的,不会出现死锁。但经常发生的情况是,一条消息在死锁后确实会死锁,并最终进入我们的错误队列 除了解决僵局的根源(这将是一个长期目标)之外,我可以想到两个选择:C# Rebus中某一消息类型的串行处理,c#,.net,message-bus,rebus,C#,.net,Message Bus,Rebus,我们有一个与第三方Web服务对话的REBS消息处理程序。由于我们无法直接控制的原因,此WCF服务经常引发异常,因为它在自己的数据库中遇到数据库死锁。然后,Rebus将尝试处理此消息五次,这在大多数情况下意味着这五次中的一次是幸运的,不会出现死锁。但经常发生的情况是,一条消息在死锁后确实会死锁,并最终进入我们的错误队列 除了解决僵局的根源(这将是一个长期目标)之外,我可以想到两个选择: 继续尝试仅使用此特定消息类型,直到成功。我最好能够设置一个超时,这样“如果有五个死锁,那么在5分钟内再试一次”,
var adapter = new Rebus.Ninject.NinjectContainerAdapter(this.Kernel);
var bus = Rebus.Configuration.Configure.With(adapter)
.Logging(x => x.Log4Net())
.Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())
.MessageOwnership(d => d.FromRebusConfigurationSection())
.CreateBus().Start();
public void Handle(TryMakeWebServiceCall message)
{
try
{
var result = client.MakeWebServiceCall(whatever);
bus.Reply(new ResponseWithTheResult{ ... });
}
catch(Exception e)
{
Data.FailedAttempts++;
if (Data.FailedAttempts < 10)
{
bus.Defer(TimeSpan.FromSeconds(1), message);
return;
}
// oh no! we failed 10 times... this is probably where we'd
// go and do something like this:
emailService.NotifyAdministrator("Something went wrong!");
}
}
以及接收端的.config:
基本上,我要寻找的是以下几点:
<rebus workers="8">
<input name="app.msg.input" error="app.msg.error" />
<input name="another.input.queue" error="app.msg.error" />
</rebus>
关于如何处理我的需求,有什么建议吗?我建议您利用saga和Rebus的超时服务来实施适合您需要的重试策略。这样,在支持REBS的web服务facade中,您可以执行以下操作:
var adapter = new Rebus.Ninject.NinjectContainerAdapter(this.Kernel);
var bus = Rebus.Configuration.Configure.With(adapter)
.Logging(x => x.Log4Net())
.Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())
.MessageOwnership(d => d.FromRebusConfigurationSection())
.CreateBus().Start();
public void Handle(TryMakeWebServiceCall message)
{
try
{
var result = client.MakeWebServiceCall(whatever);
bus.Reply(new ResponseWithTheResult{ ... });
}
catch(Exception e)
{
Data.FailedAttempts++;
if (Data.FailedAttempts < 10)
{
bus.Defer(TimeSpan.FromSeconds(1), message);
return;
}
// oh no! we failed 10 times... this is probably where we'd
// go and do something like this:
emailService.NotifyAdministrator("Something went wrong!");
}
}
您可以在其中StoreInMemory
,StoreInSqlServer
,StoreInMongoDb
,StoreInRavenDb
,或使用外部TimeoutManager
如果您选择(1),您需要检查Rebus代码并自行构建—它基本上只是一个可配置的、支持Topshelf的控制台应用程序,其中包含一个Rebus端点
如果您需要更多帮助,请告诉我-
总线。延迟
是您的系统变得很棒的地方,并且将能够克服所有导致其他系统崩溃的小故障:)好的,酷!明天我一定要看一看传奇故事。我没有立即看到如何配置Saga状态的存储位置(我们可能使用sqlserver),但我相信我可以在某个地方找到它。谢谢我会让你知道这对我是否有效。编辑:已找到位置:)我可以在这里看到一些增长潜力,因为此Web服务用于下订单,我们向客户承诺,如果他们在某个时间之前下订单,他们将在第二天交付。使用这种方法,我们可以开始发送恐慌性电子邮件/短信,以防“截止日期”临近,仍然有消息需要处理。我已经尝试过这样做,我已经走了很长的路,但我遇到了一个问题。当出现bus.Defer
时,它会序列化我定义的ISagaData
类,但在反序列化后,Newtonsoft.Json声明它无法加载在其中定义类型的程序集。消息内容(在SQL Server中):{“$type”:“CeyennePOS.Tasks.MQ.ExternalOrderSagaData,CeyennePOS.Tasks.MQ”,“OrderID”:100,“尝试次数”:1,“Id”:“4b758670-7f39-4e51-8502-85a27a34e933”,“修订版”:1}
。这个程序集是一个用assembly.LoadFile
加载的插件。顺便说一句,当我使用始终加载的“核心”程序集而不是动态加载的程序集时,它确实可以工作。问题是,我可以肯定地知道,在设置RebusIBus
时,插件程序集已经加载。所以我不知道为什么找不到。我宁愿在插件程序集中定义saga类型,这样只有那些插件才依赖于Rebus。在bus.Defer调用期间,您创建了一个新的消息实例,而不是从方法参数传递原始消息变量,这有什么特殊原因吗?
Configure.With(...)
.(...)
.Timeouts(t => // configure it here)