activemq - 等待消耗所有消息

时间:2013-11-07 23:33:50

标签: java jms activemq

我有一个案例,其中有一个处理多个项目的批量操作。处理完所有项目后,我必须将操作状态更新为已完成。项目由多个消费者并行处理。

理论上,消费者在处理项目后可以检查是否没有剩余项目(或者此行动的队列中没有消息),但是有可能两个消费者(A和B)同时完成,他们两者同时检查,他们都看到另一个尚未准备好(因为交易尚未提交) - 消费者A不会看到消费者B做出的改变,而消费者B不会看到消费者A做出的改变,所以他们都不会更新行动状态。我是对的吗?

如果没有对状态进行某种额外的定期检查并且没有其开销,如何实现这样的条件?如果每个动作有数千个项目,则定期检查可能会很好,但如果通常有1-2个长期运行的项目,则效率非常低。

谢谢!

编辑:简而言之 - 在处理一组消息后触发某些操作的正确方法是什么,但是:

  • 消息必须并行处理
  • 定期检查所有邮件是否都已处理不是答案

5 个答案:

答案 0 :(得分:2)

您可以让每个消费者在完成处理时(以及在提交自己的事务之前)将一个已完成的消息排入新队列。我们已完成的消息应该与单个消费者一起进入队列;当此使用者处理消息时,它会检查原始队列是否为空。这具有序列化检查和解决最初由并行性引起的问题的效果。

这不是一般情况下最有效的方法,但由于您提到您只有1-2个长时间运行的项目,它可能适合您。我之前在类似的情况下做过这件事并且效果很好。

答案 1 :(得分:2)

您需要一流的批处理设施。仅仅依靠队列的大小是不够可靠的。例如,您可以让一个进程处理一条消息然后拒绝它,从而将消息放回队列(以前是“空”)。

相反,使批处理成为一流的概念。考虑发送包含批次中项目数的“批量启动”消息。然后,在处理消息时,他们可以更新批处理状态记录或某些其他设备。批处理状态可以跟踪处理的消息数,通过的数量,失败的数量等

当处理完最后一条消息时,它可以通过查看处理的消息数与批次计数“减1”(因为它正在运行最后一条消息)来检查批次状态以查看它是否是“最后一条消息”。 / p>

你想要使这个过程成为原子,所以例如,如果你正在使用SQL,你会看到获取批处理状态行“FOR UPDATE”,它会将行锁定到你的事务中,从而你的比较可以是原子的。

你也可以在行上设置一个触发器,并检查一下,如果这更符合你的风格。

或者你可以在你的系统上有一个全局对象来管理这个。各种机制。

但关键是你有一些总体批量概念来管理所有工人。您不能在单个工作人员级别执行此操作,而不是可靠。

答案 2 :(得分:1)

作为Will和Dan的答案的组合,我建议一个批处理管理队列,其中带有批量大小计数器的“批量启动”消息以及消费者在完成后发送的“消息处理”消息到达处理消息。

单个管理使用者可以在处理完邮件到达时匹配已处理的邮件,直到它们与批次大小匹配为止,并记录批次的完成情况。

答案 3 :(得分:0)

要考虑错误情况,您必须定期检查。

例如,假设队列中有两个使用者和一封邮件。消费者1选择并开始处理消息。现在,消费者1意外崩溃并且事务被回滚。消息现在需要由消费者2进行提取和处理。

因此,在成功处理完所有消息之前,消费者2无法退出。检查这个的唯一方法是定期检查队列大小,直到它为空。如果消费者2刚刚在没有消息的情况下退出,那么消费者1中的队列中的未处理消息最终将回滚事务。

答案 4 :(得分:0)

创建一个 ActionMonitor ,负责将操作标记为已完成。不同的 ActionConsumer 实例将在完成后通知它。当完成的消费者数量与正在运行的消费者数量相同时, ActionMonitor 会将操作标记为已完成。

使用此解决方案,无需添加任何额外的队列或线程。将 Action 标记为已完成的实际执行将由消耗最后一个元素的同一线程执行。

喜欢这样:

public void ActionMonitor {
    private int numberOfConsumers; // Total number of consumers.
    private int numberOfConsumersFinished;

    public synchronized void consumerFinished() { // Sync could be more efficient.
        numberOfConsumersFinished++;
        if(numberOfConsumers == numberOfConsumersFinished) {
            markTheActionAsFinished();
        }
    }
}

public void ActionConsumer {

    private ActionMonitor actionMonitor;

    public void processElementsInAction() {
        while(moreElementsToProcess()) {
            takeNewElementAndProcessIt();
        }
        actionMonitor.consumerFinished();
    }
}

警告:您需要提前知道有多少消费者。

我希望它有所帮助。