2

我有一个 WCF 服务,可以将消息发布到一个私有的、非事务性的 MSMQ 队列。我有另一个 WCF 服务(多线程),它处理 MSMQ 消息并将它们插入数据库。

我的问题是测序。我希望消息按特定顺序排列。例如 MSG-A 需要在插入 MSG-B 之前进入数据库。因此,从数据库的角度来看,我目前的解决方案非常粗糙且昂贵。

我正在阅读该消息,如果它的 MSG-B 并且数据库中没有 MSG-A,我将其放回消息队列中,并继续这样做,直到将 MSG-A 插入数据库。但这是一项非常昂贵的操作,因为它涉及表扫描(SELECT stmt)。

消息总是按顺序发布到队列中。

没有使我的 WCF 队列处理服务单线程(通过将服务行为属性 InstanceContextMode 设置为 Single),有人可以提出更好的解决方案吗?

谢谢

4

2 回答 2

1

与其在将消息从队列中取出后立即将它们推送到数据库,不如在内存中保留一个待处理消息的列表。当您获得 A 或 B 时,请检查匹配的是否在列表中。如果是这样,请将它们(以正确的顺序)提交到数据库,并从列表中删除匹配的一个。否则,只需将新消息添加到该列表即可。

如果检查匹配项的任务太昂贵而无法序列化-我假设您出于某种原因正在使用多线程-您可以让另一个线程处理该列表。现有的多个线程读取,立即将大部分消息提交给 DB,但将 As 和 Bs 放在(线程安全)列表中。后台线程通过该列表查找匹配的 As 和 B,并在找到它们时以正确的顺序提交它们(并将它们从列表中删除)。

底线是 - 由于您使用多个线程从队列中删除项目,因此您将不得不在某个地方进行序列化,以确保排序。诀窍是尽量减少锁定在序列码中的次数和时间长度。

您可能还可以在数据库级别使用触发器或其他方式执行某些操作,以便在检测到这种情况时重新排序条目。恐怕我对数据库编程知之甚少,无法提供帮助。

更新:假设消息包含一些 id,可让您将消息“A”与正确的关联消息“B”相关联,以下代码将确保 A 在 B 之前进入数据库。请注意,它不能确保它们是相邻的数据库中的记录 - A 和 B 之间可能还有其他消息。此外,如果由于某种原因您得到 A 或 B 而没有收到其他类型的匹配消息,则此代码将泄漏内存,因为它挂在不匹配的消息上永远。

(您可以将这两个“锁定”块提取到一个子程序中,但为了清楚 A 和 B,我将其保留为这样。)

static private object dictionaryLock = new object();
static private Dictionary<int, MyMessage> receivedA = 
    new Dictionary<int, MyMessage>();
static private Dictionary<int, MyMessage> receivedB = 
    new Dictionary<int, MyMessage>();

public void MessageHandler(MyMessage message)
{
    MyMessage matchingMessage = null;
    if (IsA(message))
    {
        InsertIntoDB(message);
        lock (dictionaryLock)
        {
            if (receivedB.TryGetValue(message.id, out matchingMessage))
            {
                receivedB.Remove(message.id);
            }
            else
            {
                receivedA.Add(message.id, message);
            }
        }
        if (matchingMessage != null)
        {
            InsertIntoDB(matchingMessage);
        }
    }
    else if (IsB(message))
    {
        lock (dictionaryLock)
        {
            if (receivedA.TryGetValue(message.id, out matchingMessage))
            {
                receivedA.Remove(message.id);
            }
            else
            {
                receivedB.Add(message.id, message);
            }
        }
        if (matchingMessage != null)
        {
            InsertIntoDB(message);
        }
    }
    else
    {
        // not A or B, do whatever
    }
}
于 2009-05-30T07:41:11.587 回答
1

如果您是这些队列的唯一客户,您可以很容易地将时间戳添加为消息头(请参阅IDesign示例)并将“发送到”字段(有点像 Outlook 消息)也保存在数据库中。您可以按照发送顺序处理它们(基本上您在使用时移动排序逻辑)。

希望这会有所帮助,阿德里安

于 2009-07-09T17:45:09.123 回答