我正在尝试使用服务将项目保留在DB中,并为后续服务触发JMS消息以提取保留的项目,以便它可以处理相同的项目。此特定操作在单个事务中发生。但是由于种族状况,有时第二服务无法获取相应的项目,因为该项目尚未持久。
我的用例非常普遍,并且在各个论坛中都有许多与此相关的讨论。解决此问题的一种方法是使用CDI事件。我尝试了同样的方法,可以解决部分问题。伪代码如下:
@Inject
@Transaction
private Event<Item> itemEvent;
public void handleItem(Item item) {
//code to persist the item
itemEvent.fire(item);
}
@Asynchronous
public void observeAfterTransactionCompletion(@Observes(during = TransactionPhase.AFTER_SUCCESS) @Transaction Item item) {
//code to send JMS message to the second service
}
我唯一的问题是当持久性成功并且事件被触发时,如果Jboss服务器出现故障,就在观察者开始处理事件之前,由于JMS消息将不会发送,第二个服务将不会得到通知。
CDI容器能否在内部处理这种情况,以便始终调用该事件?是否会在服务器启动时通知观察者自动启动?如果没有,我该如何处理这种情况,以便我的方法是万无一失的?
注意::我已经尝试了第二种服务中的重试方法,直到消息可用。另外,将事件保留在另一个队列中是一种替代方法,这似乎非常乏味。寻找一种聪明的方法。
更新:我的初始代码的编写方式是持久性和消息传递都在单个事务中。但这导致第二服务消耗了消息,甚至没有成功实现第一服务持久性也导致了错误,因为第二服务找不到所需的数据,这些数据仍将被持久保存。
更新2:初始方法伪代码如下:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processMessage() {
// code to persist data to DB
// code to publish JMS message to the consumer
}
即使使用xa-datasouce和xa-connection工厂,问题仍然存在。
答案 0 :(得分:1)
一种方法是使用XA事务。
JMS和数据库资源都加入同一个事务,因此在提交时会触发JMS消息,数据库更新错误或JMS消息发送错误都会触发整个操作的回滚。
请注意,您必须使用xa-datasource
更改数据源定义,更新JMS连接工厂配置以使用xa,并且必须也处理JMS会话
<!-- JMS connection factory configuration -->
<pooled-connection-factory name="myCxFactory">
<transaction mode="xa"/>
[...]
</pooled-connection-factory>
// JMS session creation (message producer)
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
还请注意,只有消息产生是事务的一部分,而不是消息的消耗
答案 1 :(得分:0)
CDI事件不是持久性的,因此在服务器重新启动后不会重新触发它们。
最后,我们最终通过在发布消息之前使用数据库持久性来解决此问题,并将持久性和发布分为两个不同的事务。但是,如果发生消息发布错误或服务器在提交消息之前重新启动服务器,我们需要自己处理消息重复。