使用事务的MSMQ接收-回滚不会使消息再次可用

9

我有一个名为“MessageQueueReceive”的类,其中包含以下内容。

public MessageQueueTransaction BlockingReceive(out Message message)
{
    MessageQueueTransaction tran = null;
    message = null;

    tran = new MessageQueueTransaction();

    tran.Begin();
    try
    {
        message = Queue.Receive(new TimeSpan(0, 0, 5), tran);
    }
    catch (MessageQueueException ex)
    {
        // If the exception was a timeout, then just continue
        // otherwise re-raise it.
        if (ex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
            throw ex;
    }

    return tran;

}

然后我的处理循环如下:-
while (!Abort)
{
    try
    {
        tran = this.Queue.BlockingReceive(out msg);

        if (msg != null)
        {
            // Process message here

            if (tran != null)
                tran.Commit();
        }
    }
    catch (Exception ex)
    {
        if (tran != null)
            tran.Abort();

    }
}

控制面板工具显示我使用的消息队列是事务型的,但未启用日志队列。
此代码创建了该队列:-
private static MessageQueue CreateMessageQueue(string queueName, bool transactional = false)
{
    MessageQueue messageQueue = MessageQueue.Create(queueName, transactional);
    messageQueue.SetPermissions("Administrators", MessageQueueAccessRights.FullControl,
            AccessControlEntryType.Allow);
    return messageQueue;
}

当调用时,将事务参数设置为“true”。

我发现,在处理消息过程中发生异常时,会调用tran.Abort,但在那时我希望消息被返回到队列中。然而,这并没有发生,消息丢失了。

我是否漏掉了一些明显的东西?有人能看出我做错了什么吗?


你接收到的队列是否在监听服务本地? - tom redfern
是的,发送和接收过程以及队列都在同一台机器上。它们是私有队列。我还在几个不同的机器上运行相同的代码,并且在所有机器上都看到了相同的问题。这表明它与此代码相关,而不是机器的某些奇怪问题。 - JohnCC
很明显的问题。你确定它在中止异常时出现了异常吗?可能是在你的消息处理代码中抛出了异常,然后被捕获并且被丢弃了,所以它从未冒泡到带有中止的异常处理程序中吗? - Iain Kelwick
2个回答

5
感谢所有的评论。我按照Russell McClure的建议重新组织了代码,并尝试创建简单的测试用例,但无法复现问题。
最终,问题根本不在我寻找的地方(这种情况有多少次?)。
在我的流水线中,我有一个重复消息检查器。我的系统处理的“消息”来自WAN上的远程设备,偶尔会有重复的消息传输。
当从MSMQ中拉取一条消息时,它将通过重复检查器和数据库写入器进行。如果数据库写入器失败,则重复检查器不会从其表中删除哈希值。当进程尝试再次循环时,由于数据库写入器失败,MSMQ事务已被回滚,因此它将再次从队列中获取相同的消息。但是,在第二次尝试时,重复检查器会发现它之前已经看到过该消息,并会悄悄地吞噬它。
解决方法是让重复检查器发现链中下一个链接发生异常,并回滚它所做的任何事情。

1

你需要将队列创建为事务性队列才能得到你想要的。

enter image description here

编辑:

如果您的队列是事务性的,那么这表明您正在处理事务时出现了问题,尽管我无法具体看到发生了什么。我建议将您的BlockingReceive方法更改为返回消息。我会将MessageQueueTransaction的创建移动到外部方法中。如果您在同一方法中调用Begin、Commit和Abort方法,则代码将更易于维护。


嗨!队列肯定是作为事务性队列创建的。如果我在“计算机管理”->“服务和应用程序”->“消息队列”中查看队列属性,它会显示为事务性。 - JohnCC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接