Spring / RabbitMQ:事务管理

时间:2013-07-02 16:53:15

标签: spring transactions rabbitmq distributed-transactions

为了简化我的问题,我有

App1与@Transactionnal方法createUser():

  • 在数据库中插入新用户
  • 在RabbitMQ中添加异步消息,以便用户收到通知邮件
  • (可能是一些额外的代码,但不多)

App2与RabbitMQ消息消费者

  • 实时填写邮件队列中的消息
  • 读取数据库中的邮件数据
  • 发送邮件

问题在于,有时App2会在App1上提交事务之前尝试使用RabbitMQ消息。这意味着App2无法读取数据库上的邮件数据,因为尚未创建用户。

有些解决方案可能是:

  • 在App2上使用READ_UNCOMMITED隔离级别
  • 在RabbitMQ消息传递中添加一些延迟(或在消费者处添加一些RetryTemplate)
  • 更改发送电子邮件的方式......

我已经看到Spring中有一个RabbitTransactionManager,但我无法理解它应该如何工作。事务处理内容似乎总是有点难以理解,文档也没有那么多帮助。


有没有办法做这样的事情?

  • 在@Transactionnal方法
  • 中向RabbitMQ队列添加消息
  • 当事务结束时,消息将提交到队列,并且更改将提交到数据库
  • 这样在db事务结束之前无法完成消息

如何?如果我发送同步RabbitMQ消息而不是异步消息,那么可以期待什么呢?它会阻止等待响应的线程吗? 因为我们确实为不同的用例发送同步和异步消息。

2 个答案:

答案 0 :(得分:2)

我知道这已经晚了,但由于我当时对@Transactional的理解有限,我也遇到了同样的问题。所以对于碰巧遇到这种情况的其他人来说,这更为重要。

当使用@Transactional将数据保存到数据库时,保存到数据库实际上不会发生,直到方法返回,而不是在调用save时。

所以,如果你有像

这样的方法
@Transactional(readOnly=false)
public void save(Object object) { //Object should be one of your entities
  entityManager.persist(object); //or however you have it set up
  rabbitTemplate.convertAndSend(message); //again - however yours is
}

即使您在将消息放入队列之前调用对象上的持久性,在方法返回之前实际上不会发生持久化,从而导致在方法返回之前和数据之前将消息放入队列实际上是在数据库中。

可以嵌套@Transactional方法(不直接)可以在save()方法返回后将消息放入队列。但是,您无法将消息放入队列并期望它不被消耗。一旦它消失了。因此,如果需要,请延迟将其放入队列。

如果要以同步方式从队列接收响应。在我的函数示例中 - 您可以执行此操作,但是它只会使实际持久化数据需要更长时间,因为它将在方法返回并实际持久保存数据之前等待来自worker的响应。 (还要记住,从排队的消息中接收响应有一个超时)。

所以我建议不要将这两个操作放在同一个@Transactional

答案 1 :(得分:1)

我对@Transactionnal和spring并不熟悉,但在AMQP中,标准消息队列不是事务操作,因此您必须将数据存储在db中(如果db connection是事务性 - 提交事务)并且仅在发送消息之后经纪人。

正确的工作流程就像App1: createUser -> notifyUser; App2: listenForNotifications

一样