即使没有异常,NServiceBus正在重试消息

时间:2012-04-11 21:37:37

标签: nhibernate nservicebus nservicebus3

我遇到了一个奇怪的问题,NServiceBus重复了X次消息,尽管处理程序中没有抛出异常。有一些信息涉及NHibernate会话和NSB ambiant事务。由于没有抛出任何错误,我不能100%确定问题,因此无法真正决定该怎么做。

我将NSB配置为Castle Windsor,如下所示:

IWindsorContainer container = new WindsorContainer(new XmlInterpreter());
container.Install(new ContainerInstaller());
container.Install(new UnitOfWorkInstaller(AppDomain.CurrentDomain.BaseDirectory, Castle.Core.LifestyleType.Scoped));
container.Install(new FactoryInstaller(AppDomain.CurrentDomain.BaseDirectory));
container.Install(new RepositoryInstaller(AppDomain.CurrentDomain.BaseDirectory));

Configure.With()
    .CastleWindsorBuilder(container)
    .FileShareDataBus(Properties.Settings.Default.DataBusFileSharePath)
    .MsmqTransport()
        .IsTransactional(true)
        .PurgeOnStartup(false)
    .UnicastBus()
         .LoadMessageHandlers()
         .ImpersonateSender(false)
     .JsonSerializer();

UnitOfWorkInstaller注册工作单元(NHibernate会话),如下所示:

public void Install(IWindsorContainer container, IConfigurationStore store)
{
    var fromAssemblyDescriptor = AllTypes.FromAssemblyInDirectory(new AssemblyFilter(_installationPath));
    container.Register(fromAssemblyDescriptor
        .IncludeNonPublicTypes()
        .Pick()
        .If(t => t.GetInterfaces().Any(i => i == typeof(IUnitOfWork)) && t.Namespace.StartsWith("Magma"))
        .WithService.AllInterfaces()
        .Configure(con => con.LifeStyle.Is(_lifeStyleType).UsingFactoryMethod(k => k.Resolve<IUnitOfWorkFactory>().Create())));
}

因此,每次消息到达时,所有存储库都使用相同的工作单元。我读到手动回滚当前事务导致错误(我真的不知道为什么)并且我也知道NSB为每个传输消息创建一个子容器,并且在处理消息之后处理该子容器。问题是当子容器被处理时,工作单元以这种方式处理:

    public void Dispose()
    {
        if (!_isDisposed)
        {
            DiscardSession();
            _isDisposed = true;
        }
    }

    private void DiscardSession()
    {
        if (_transaction != null && _transaction.IsActive)
        {
            _transaction.Dispose();
        }
        if (Session != null)
        {
            Session.Dispose();
        }
    }

我的处理程序结构如下:( _unitOfWork作为构造函数依赖项传递)

    public void Handle(<MessageType> message)
    {
        using (_unitOfWork)
        {
            try
            {
                // do stuff
                _unitOfWork.Commit();
            }
            catch (Exception ex)
            {
                _unitOfWork.Rollback();

                // rethrow so the message stays in the queue
                throw;
            }
        }
    }

我发现如果我不提交工作单元(刷新会话并提交事务),我会收到一条错误消息,指出该消息已经重试超过最大重试次数bla bla bla ...

所以它似乎与NHibernate会话以及它的创建和处理方式有关,但由于它是在工作单元内创建的,所以我无法真正使用会话工厂。我读到我可以使用IMessageModule来创建和处理会话,但我不知道这是否是正确的方法,因为我不知道是什么原因造成了错误。

所以回顾一下:

  • 我正在使用一个范围的工作单元,以便使用它的所有处理程序依赖项将共享同一个实例(thx到子容器,BTW:我已将工作单元设置为瞬态思维,即子容器会将所有瞬态对象视为该容器中的单例,但我看到工作单元未被共享,因此这就是为什么它设置为作用域的原因)

  • 我将处理程序包装在using(_unitOfWork) { }语句中,以便在每次处理后处理工作单元。

  • 当工作单位被处置时,NHibernate会话也被处理

  • 如果我没有在Commit上明确调用_unitOfWork,则消息会重试超出最大重试次数,然后会引发错误。

导致此行为的原因是什么?并且IMessageModule是答案吗?

1 个答案:

答案 0 :(得分:0)

我想我把它缩小了一点......我删除了所有using(_unitOfWork)_unitOfWork.Commit()_unitOfWork.Rollback()并让NSB TransactionScope完成工作提交或回滚事务,因为NHibernate的Session正在NSB事务范围中登记。

我也开始使用NHibernate会话的事务(Session.Transaction)而不是通过Session.BeginTransaction()获取对它的引用并使用它。我已经复制/粘贴了我的UoW实现,因此您可以看到差异(旧代码在评论中)。

我不知道我的更改是否占用了什么,除了使用Session的事务并删除了刷新,因为它在事务提交中处理似乎已经解决了问题...我没有必须明确调用Commit方法,以便再次成功处理消息。这是我的UoW实现:

public class NHibernateUnitOfWork : INHibernateUnitOfWork
{
    //private ITransaction _transaction;
    private bool _isDisposed;
    private bool _isInError;

    public ISession Session { get; protected set; }

    public NHibernateUnitOfWork(ISession session)
    {
        Contract.Requires(session != null, "session");
        Session = session;

        //_transaction = Session.BeginTransaction();

        // create a new transaction as soon as the session is available
        Session.BeginTransaction();
        _isDisposed = false;
        _isInError = false;
    }

    public void MarkCreated(Object entity)
    {
        // assert stuff

        try
        {
            Session.SaveOrUpdate(entity);
            //Session.Flush();
        }
        catch (HibernateException ex)
        {
            HandleError();
            throw;
        }
    }

    public void MarkUpdated(Object entity)
    {
        // assert stuff

        try
        {
            Session.Update(entity);
            //Session.Flush();
        }
        catch (HibernateException ex)
        {
            HandleError();
            throw;
        }
    }

    public void MarkSavedOrUpdated(Object entity)
    {
        // assert stuff

        try
        {
            Session.SaveOrUpdate(entity);
            //Session.Flush();
        }
        catch (HibernateException)
        {
            HandleError();
            throw;
        }
    }

    public void MarkDeleted(Object entity)
    {
        // assert stuff

        try
        {
            Session.Delete(entity);
            //Session.Flush();
        }
        catch (HibernateException ex)
        {
            HandleError();
            throw;
        }
    }

    public void Commit()
    {
        // assert stuff

        try
        {
            //Session.Flush();
            //_transaction.Commit();
            Session.Transaction.Commit();
        }
        catch (HibernateException ex)
        {
            HandleError();
            throw;
        }
    }

    public void Rollback()
    {
        // assert stuff

        try
        {
            //if (!_transaction.WasRolledBack)
            //{
            //    _transaction.Rollback();
            //}
            Session.Transaction.Rollback();
        }
        catch (HibernateException ex)
        {
            HandleError();
            throw;
        }
    }

    public void Dispose()
    {
        if (!_isDisposed)
        {
            DiscardSession();
            _isDisposed = true;
        }
    }

    private void DiscardSession()
    {
        //if (_transaction != null && _transaction.IsActive)
        //{
        //    _transaction.Dispose();
        //}
        if (Session != null)
        {
            try
            {
                // rollback all uncommitted changes
                if (Session.Transaction != null && Session.Transaction.IsActive)
                {
                    Session.Transaction.Rollback();
                }
                //Session.Clear();
                Session.Close();
            }
            catch (Exception)
            { }
            finally
            {
                Session.Dispose();
            }
        }
    }

    private void HandleError()
    {
        _isInError = true;
        //if (_transaction != null && _transaction.IsActive)
        //{
        //    _transaction.Rollback();
        //}
        if (Session.Transaction != null && Session.Transaction.IsActive)
        {
            Session.Transaction.Rollback();
        }
    }

    // assert methods
}

这有什么意义吗?我仍然不知道是什么原因造成了错误,但它似乎与在事务范围完成之前处理NHibernate会话有关。