MSMQ通过事务接收 - 回滚不再使消息可用

时间:2011-11-15 22:35:19

标签: c# .net transactions msmq message-queue

我在一个名为“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,但此时我希望将消息返回到队列中。但是,这种情况并没有发生,消息也会丢失。

我错过了一些明显的东西吗?谁能看到我做错了什么?

2 个答案:

答案 0 :(得分:5)

感谢所有评论。我确实重新组织了我的代码,正如Russell McClure建议的那样,我试图创建简单的测试用例,但无法重现问题。

最后,问题完全不在我看的地方(这种情况多久发生一次?)。

在我的管道中,我有一个重复的邮件检查器。我的系统处理的“消息”来自WAN上的远程设备,有时候线路上的消息也是重复的。

当从MSMQ中提取消息时,它将通过重复检查程序传递给数据库编写器。如果数据库写程序失败,则检查的重复项不会从其表中删除哈希。当进程再次尝试循环时,它将从队列agan获取相同的消息,因为MSMQ事务已在数据库写入程序失败时回滚。但是,在第二次尝试时,重复检查器会发现它之前已经看到过该消息,并以静默方式吞下它。

修复方法是让重复检查器发现来自链中下一个链接的异常,并回滚它所做的任何事情。

答案 1 :(得分:1)

您的队列需要创建为事务性队列才能获得所需的内容。

enter image description here

编辑:

好吧,如果您的队列是事务性的,那么这表明您错误处理了您的事务,尽管我无法具体了解它是如何发生的。我会更改 BlockingReceive 方法以返回消息。我会将 MessageQueueTransaction 的创建移动到外部方法。如果您使用相同的方法调用Begin,Commit和Abort方法,则代码将更易于维护。