我有一个类似于以下的处理程序,它基本上响应命令并将一大堆命令发送到不同的队列。
public void Handle(ISomeCommand message)
{
int i=0;
while (i < 10000)
{
var command = Bus.CreateInstance<IAnotherCommand>();
command.Id = i;
Bus.Send("target.queue@d1555", command);
i++;
}
}
此块的问题是,在循环完全完成之前,目标队列或传出队列中都不会显示任何消息。有人可以帮我理解这种行为吗?
此外,如果我使用Tasks在Handler中发送消息,如下所示,则会立即显示消息。所以有两个问题,
在消息处理程序中使用任务有什么后果吗?
public void Handle(ISomeCommand message)
{
int i=0;
while (i < 10000)
{
System.Threading.ThreadPool.QueueUserWorkItem((args) =>
{
var command = Bus.CreateInstance<IAnotherCommand>();
command.Id = i;
Bus.Send("target.queue@d1555", command);
i++;
});
}
}
非常感谢您的时间!
答案 0 :(得分:4)
第一个问题:从一个队列中挑选一条消息,为它运行所有已注册的消息处理程序以及任何其他事务操作(如写入新消息或对数据库写入)在一个事务中执行。要么全部完成,要么都不完成。所以你看到的是预期的行为:从队列中选择一条消息,处理ISomeCommand并写入10000个新的IAnotherCommand要么完全完成,要么都不完成。要避免此行为,您可以执行以下操作之一:
将NServiceBus端点配置为不是事务性
public class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher,IWantCustomInitialization
{
public void Init()
{
Configure.With()
.DefaultBuilder()
.XmlSerializer()
.MsmqTransport()
.IsTransactional(false)
.UnicastBus();
}
}
在禁止环境事务的事务范围中包含IAnotherCommand的发送。
public void Handle(ISomeCommand message)
{
using (new TransactionScope(TransactionScopeOption.Suppress))
{
int i=0;
while (i < 10000)
{
var command = Bus.CreateInstance();
command.Id = i;
Bus.Send("target.queue@d1555", command);
i++;
}
}
}
在另一个线程上发出Bus.Send,通过自己启动一个新线程,使用System.Threading.ThreadPool.QueueUserWorkItem或Task类。这是有效的,因为环境事务不会自动转移到新线程。
第二个问题:使用“任务”或我提到的任何其他方法的后果是,您没有对整个事务进行事务性保证。
如何处理生成5000 IAnotherMessage且电源突然熄灭的情况?
如果使用2)或3)原始ISomeMessage将无法完成,并且当您再次启动端点时,NServiceBus将自动重试。最终结果:5000 + 10000 IAnotherCommands。
如果你使用1)你将完全失去IAnotherMessage并最终只有5000个IAnotherCommands。
使用推荐的事务方式,初始的5000 IAnotherCommands将被丢弃,原始的ISomeMessage返回队列,并在端点再次启动时重试。最终结果:10000个IAnotherCommands。
答案 1 :(得分:0)
如果内存服务NServiceBus包含TransactionScope
中消息处理程序的调用,如果使用了事务选项并且TransactionScope
需要一些帮助以实现跨线程友好:
答案 2 :(得分:0)
如果您尝试减少开销,还可以捆绑您的邮件。发送的签名是Bus.Send(IMessage []消息)。如果您可以保证不会破坏MSMQ的大小限制,那么您可以立即发送()所有消息。如果大小限制是一个问题,那么您可以将它们分块或使用数据总线。