我们注意到消息处理程序存在长时间运行请求的问题,如果请求在分布式事务超时之前没有完成处理,则在原始消息处理之前或之前在新线程上重新处理相同的消息原始消息甚至引发错误。消息最终会到达错误队列。
以下是我们如何转载此问题:
将本地计算机配置更新为事务的默认超时1分钟:
<system.transactions>
<defaultSettings distributedTransactionManagerName="" timeout="00:01:00" />
<machineSettings maxTimeout="00:01:00" />
</system.transactions>
将NSB消息服务配置为多线程:
<MsmqTransportConfig NumberOfWorkerThreads="2" MaxRetries="2" />
然后在我们的处理程序中,一个线程休眠了2分钟(下面的代码),会发生的情况是工作线路A上有消息。一旦发生1分钟超时,就会在工作线程B和工作线程上处理相同的消息A还在睡觉。工作线程A最终返回并出现“无法在事务中登记”的错误,然后再次重新开始处理消息,这将在两个工作线程上继续,直到消息最终进入错误队列。
public void Handle(RequestDataMessage message)
{
Logger.Info("==========================================================================");
Logger.InfoFormat("Received request {0}.", message.DataId);
Logger.InfoFormat("String received: {0}.", message.String);
Logger.InfoFormat("Header 'Test' = {0}.", message.GetHeader("Test"));
Logger.InfoFormat(Thread.CurrentPrincipal != null ? Thread.CurrentPrincipal.Identity.Name : string.Empty);
var response = Bus.CreateInstance<DataResponseMessage>(m =>
{
m.DataId = message.DataId;
m.String = message.String;
});
response.CopyHeaderFromRequest("Test");
response.SetHeader("1", "1");
response.SetHeader("2", "2");
Thread.Sleep(new TimeSpan(0, 2, 0));
Logger.Info("========== Thread continued ===========");
Logger.InfoFormat("Received request {0}.", message.DataId);
Logger.InfoFormat("String received: {0}.", message.String);
Logger.InfoFormat("Header 'Test' = {0}.", message.GetHeader("Test"));
Logger.InfoFormat(Thread.CurrentPrincipal != null ? Thread.CurrentPrincipal.Identity.Name : string.Empty);
Bus.Reply(response); //Try experimenting with sending multiple responses
}
这是一个已知问题,还是我们应该设计代码以避免发生这种类型的密集重新处理?
答案 0 :(得分:4)
长期持有交易并不是一个好主意。这意味着参与该事务的所有资源都被锁定。您应该尝试拆分处理程序中正在进行的工作。 处理程序处理的工作类型是什么?分裂那项工作有可能吗?
关于另一个线程问题:当线程A正在休眠时,线程B处理相同的消息,我在版本3.3.8或版本4.0.2中没有看到该行为
这是我的处理程序:
public void Handle(ProcessSomething message)
{
Console.WriteLine("{0} -- Thread {1} Handling MessageId {2}", DateTime.Now, Thread.CurrentThread.ManagedThreadId, Bus.CurrentMessageContext.Id);
var response = Bus.CreateInstance<DataResponseMessage>(m =>
{
m.DataId = message.DataId;
m.String = message.String;
});
Thread.Sleep(new TimeSpan(0, 1, 10));
Console.WriteLine("{0} -- Thread {1} contiuned after sleep MessageId {2}", DateTime.Now, Thread.CurrentThread.ManagedThreadId, Bus.CurrentMessageContext.Id);
Bus.Reply(response); //Try experimenting with sending multiple responses
}
我在队列中放了4条消息,将线程数/ MaxConcurrency设置为2,并将machine.config maxtimeout设置为1分钟,并将处理程序设置为休眠1分钟10秒。
Bus.Reply将无法登记交易。因为事务超时。这会导致异常并重试该消息。在同一个处理程序中发送的所有消息都是同一事务的一部分。因此,此消息失败并重试。 我看到的是线程'A'处理Msmq消息Id'm1'睡眠继续使用相同的消息'm1'并在尝试回复时抛出。
,当线程'A'正在休眠时,我看不到线程'B'拾取'm1'干杯。
答案 1 :(得分:1)
有一个类似的问题,事务超时,但不想更改每个事务的默认超时,所以最终只是更改NServiceBus的事务配置。
Configure.Transactions.Advanced(x => x.DefaultTimeout(TimeSpan.FromMinutes(3)));
这有点不理想,并建议将其分解为更多模块化的工作。