减少并发消息传递系统的DB WRITE争用

时间:2014-02-13 16:10:43

标签: java multithreading concurrency apache-camel activemq

请注意:虽然我的问题特别涉及Camel(2.11.0)和ActiveMQ(5.8.0),但它确实是关于并发消息传递解决方案的正确设计,并且可以想象得到任何有任何强大的消息和/或并发经验的人都会回答。

我有一个Camel路由,首先从ActiveMQ队列(myQueue)读取消息并将它们发送到bean(processorBean)进行处理:

<camelContext id="my-camel-context" xmlns="http://camel.apache.org/schema/spring">
    <endpoint id="myQueue" uri="myBroker01:queue:myQueue" />

    <route id="my-route">
        <from ref="myQueue" />
        <to uri="bean:processorBean?method=process" /> 
    </route>
</camelContext>

public class ProcessorBean {
    public void process(Exchange exchange) {
        String messageJSON = (String)exchange.getIn().getBody();

        // Example: now messageID might be "12345"
        String messageID = parseJSON(messageJSON, "messageID");

        // Look up DB records based on this messageID.
        // The same messageID will *always* return the same list of widgets.
        List<Widget> widgets = dao.getWidgetsByMessageID(messageID);

        // Make updates to widgets.
        for(Widget widget : widgets) {
            widget.setFizz(true);
            widget.setBuzz("Yahtzee!!!");
        }

        // Persist all updates to the widget list.
        dao.updateAll(widgets);
    }
}

这个bean使用消息的ID(一个字符串messageID字段)来查找数据库中的一堆记录,对它们进行更改,然后保存它们。注意消息从我无法控制的外部进程到达myQueue时,非常非常重要。换句话说,我无法阻止在线程上显示具有相同messageID值的消息(以下称为“重复”或重复消息)。因此,此外部流程可以向myQueue发送1,000封邮件,其中20封可能都有messageID=12345

目前,我只配置了1个Camel使用者运行(因此它是“单线程”)。因此,当重复显示时,目前没有任何损害(除了可能的不必要的性能问题)。每个消息一次处理一个,如果有20条消息具有相同的messageID,那么相同的DB记录会反复获得相同的(不必要的)更新。对性能不好,当然,但它不会产生“坏数据”,脏写,我们的产品竞争条件等。

我现在想要在等式中添加更多Camel使用者线程,以便可能有10个消费者线程都读取myQueue

显然,现在我们有可能在数据库中进行WRITE争用。假设myQueue上有2封邮件,并且都有messageID=12345。一个Camel消费者线程读取第一条消息,另一个线程同时读取第二条消息。每个线程将其消息路由到其自己的processorBean副本/版本。两个processorBean实例大约在同一时间执行,使用messageID从DB中读取相同的记录,在内存中对它们执行相同的操作,然后调用dao.updateAll(...)到WRITE同时对相同记录的更改。如果两个线程同时更新相同的DB记录,则会出现争用。

另一个重要的注意事项是,更改数据库(由另一个团队控制)以实现分片,乐观锁定等等,在这种情况下不是一个选项(背景故事太长)。

我的问题:在这种情况下,在Java层可以做些什么来缓解WRITE争用?必须从应用程序内部处理WRITE争用。想法?

1 个答案:

答案 0 :(得分:0)

您可以使用像这样的全局锁

synchronized(ProcessorBean.class) {
    // Persist all updates to the widget list.
    dao.updateAll(widgets);
}