Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 缓解并发消息传递系统中的数据库写入争用_Java_Multithreading_Concurrency_Apache Camel_Activemq - Fatal编程技术网

Java 缓解并发消息传递系统中的数据库写入争用

Java 缓解并发消息传递系统中的数据库写入争用,java,multithreading,concurrency,apache-camel,activemq,Java,Multithreading,Concurrency,Apache Camel,Activemq,请注意:尽管我在这里的问题特别涉及到Camel(2.11.0)和ActiveMQ(5.8.0),但它实际上是关于并发消息解决方案的正确设计,任何具有强大消息和/或并发经验的人都可以很好地回答 我有一个驼峰路由,首先从ActiveMQ队列(myQueue)读取消息,然后将它们发送到bean(processorBean)进行处理: <camelContext id="my-camel-context" xmlns="http://camel.apache.org/schema/spring"&

请注意:尽管我在这里的问题特别涉及到Camel(2.11.0)和ActiveMQ(5.8.0),但它实际上是关于并发消息解决方案的正确设计,任何具有强大消息和/或并发经验的人都可以很好地回答

我有一个驼峰路由,首先从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>

以及:

公共类处理器bean{
公共作废流程(交换){
String messageJSON=(String)exchange.getIn().getBody();
//示例:现在messageID可能是“12345”
字符串messageID=parseJSON(messageJSON,messageID);
//根据此messageID查找数据库记录。
//相同的messageID将*始终*返回相同的小部件列表。
List widgets=dao.getWidgetsByMessageID(messageID);
//对小部件进行更新。
用于(小部件:小部件){
widget.setFizz(true);
setBuzz(“Yahtzee!!!”);
}
//将所有更新持久化到小部件列表。
dao.updateAll(小部件);
}
}
这个bean使用消息的ID(字符串
messageID
字段)在数据库中查找一组记录,对它们进行更改,然后保存它们。同样重要的是要注意,消息从我无法控制的外部进程到达我的队列。换句话说,我无法阻止线程上显示具有相同
messageID
值的消息(以下称为“重复”或重复消息)。因此,这个外部进程可以将1000条消息发送到
myQueue
,其中20条消息都可以具有
messageID=12345

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

现在,我想在等式中添加更多的Camel消费线程,这样可能会有10个消费线程全部读取
myQueue

显然,现在我们有可能在数据库中发生写争用。假设在
myQueue
上有两条消息,并且都有
messageID=12345
。一个驼峰消费者线程读取第一条消息,另一个线程同时读取第二条消息,或者差不多。每个线程将其消息路由到自己的
processorBean
副本/版本。两个
processorBean
实例几乎同时执行,使用
messageID
从数据库中读取相同的记录,在内存中对它们执行相同的操作,然后调用
dao.updateAll(…)
将更改同时写入相同的记录。如果两个线程同时更新相同的DB记录,则会发生争用

另一个重要的注意事项是,更改DB(由另一个团队控制)以实现切分、乐观锁定等功能在这种情况下不是一个选项(后台故事太长)


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

您可以使用这样的全局锁

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

大多数数据库可以轻松地处理多个写操作。你在使用什么数据库?谢谢@Peter Lawrey(+1)-我们在Sybase 15上…我知道,我知道…(再说一遍,我无法控制这一切)。另外,为了解决这个问题,让我们假设DB不能处理多个写操作。这里的纯Java解决方案是什么?即使如此,它也应该能够处理并发写入。如果不能,您可以使用单线程执行器将所有写操作排队到一个线程,或者在Java中使用全局写锁。实现序列化写操作可能会从多个使用者身上消除任何好处,因为大部分延迟将在数据库中。Sybase 15支持
选择更新
语法。使用该选项,您可以序列化更新,而无需进行任何代码更改,但现在必须跨越
选择
更新
的事务除外。但如上所述,这将使消息队列中的并行消费成为一个情绪点。也许除了更高的可用性。
synchronized(ProcessorBean.class) {
    // Persist all updates to the widget list.
    dao.updateAll(widgets);
}