10

为了简化我的问题,我有

带有@Transactionnal 方法createUser() 的App1:

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

带有 RabbitMQ 消息消费者的 App2

  • 实时使用邮件队列中的消息
  • 读取数据库中的邮件数据
  • 发邮件

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

一些解决方案可能是:

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

我已经看到 Spring 中有一个 RabbitTransactionManager,但我不明白它应该如何工作。事务处理的内部结构似乎总是有点难以理解,文档也没有太大帮助。


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

  • 在 @Transactionnal 方法中向 RabbitMQ 队列添加消息
  • 事务结束时,将消息提交到队列,并将更改提交到数据库
  • 使消息在DB事务结束前不能被消费

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

4

2 回答 2

3

我知道这已经晚了,但由于我当时对@Transactional 的了解有限,我遇到了同样的问题。因此,对于您碰巧偶然发现的其他任何人来说,这更有用。

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

因此,如果您有类似的方法

@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()方法返回后将消息放在队列中。但是,您不能将消息放入队列并期望它不会被使用。一旦它消失了。因此,如果需要,请延迟将其放入队列中。

如果您想以同步方式接收来自队列的响应。在我的函数示例中 - 你可以这样做,但是它只会使实际持久化数据需要更长的时间,因为它会在方法返回并实际持久化数据之前等待工作人员的响应。(还请记住,接收来自排队消息的响应有超时)。

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

于 2016-02-05T16:30:30.403 回答
1

我对@Transactionnal 和spring 不是很熟悉,但是在AMQP 标准消息队列中不是事务性操作,所以你必须将数据存储在db 中(如果db 连接是事务性的——提交事务),然后才向代理发送消息。

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

于 2013-07-03T10:31:49.833 回答