递归Bus.Send()with-in Handler(Transactions,Threading,Tasks)

时间:2012-11-14 20:26:31

标签: nservicebus

我有一个类似于以下的处理程序,它基本上响应命令并将一大堆命令发送到不同的队列。

    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中发送消息,如下所示,则会立即显示消息。所以有两个问题,

  1. 基于任务的发送的解释是什么?
  2. 在消息处理程序中使用任务有什么后果吗?

    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++;
            });
        }
    }
    
  3. 非常感谢您的时间!

3 个答案:

答案 0 :(得分:4)

第一个问题:从一个队列中挑选一条消息,为它运行所有已注册的消息处理程序以及任何其他事务操作(如写入新消息或对数据库写入)在一个事务中执行。要么全部完成,要么都不完成。所以你看到的是预期的行为:从队列中选择一条消息,处理ISomeCommand并写入10000个新的IAnotherCommand要么完全完成,要么都不完成。要避免此行为,您可以执行以下操作之一:

  1. 将NServiceBus端点配置为不是事务性

    public class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher,IWantCustomInitialization
    {
        public void Init()
        {
            Configure.With()
                .DefaultBuilder()
                .XmlSerializer()
                .MsmqTransport()
                .IsTransactional(false)
                .UnicastBus();
        }
    }
    
  2. 在禁止环境事务的事务范围中包含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++; 
            } 
        } 
    } 
    
  3. 在另一个线程上发出Bus.Send,通过自己启动一个新线程,使用System.Threading.ThreadPool.QueueUserWorkItem或Task类。这是有效的,因为环境事务不会自动转移到新线程。

  4. 第二个问题:使用“任务”或我提到的任何其他方法的后果是,您没有对整个事务进行事务性保证。

    如何处理生成5000 IAnotherMessage且电源突然熄灭的情况?

    如果使用2)或3)原始ISomeMessage将无法完成,并且当您再次启动端点时,NServiceBus将自动重试。最终结果:5000 + 10000 IAnotherCommands。

    如果你使用1)你将完全失去IAnotherMessage并最终只有5000个IAnotherCommands。

    使用推荐的事务方式,初始的5000 IAnotherCommands将被丢弃,原始的ISomeMessage返回队列,并在端点再次启动时重试。最终结果:10000个IAnotherCommands。

答案 1 :(得分:0)

如果内存服务NServiceBus包含TransactionScope中消息处理程序的调用,如果使用了事务选项并且TransactionScope需要一些帮助以实现跨线程友好:

TransactionScope and multi-threading

答案 2 :(得分:0)

如果您尝试减少开销,还可以捆绑您的邮件。发送的签名是Bus.Send(IMessage []消息)。如果您可以保证不会破坏MSMQ的大小限制,那么您可以立即发送()所有消息。如果大小限制是一个问题,那么您可以将它们分块或使用数据总线。