请注意:虽然我的问题特别涉及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争用。想法?
答案 0 :(得分:0)
您可以使用像这样的全局锁
synchronized(ProcessorBean.class) {
// Persist all updates to the widget list.
dao.updateAll(widgets);
}