0

我的路线如下所示

from("jms:queue:IN_QUEUE) //(A) Transactional Endpoint .transacted("required") //(B) TX Policy with PROPAGATION_REQUIRED and JPATxManager .bean("someBean", "readFromDB()") //(C) Read from DB .bean("someBean", "writeToDB()") //(D) Write to DB .to("file:/home/src?fileName=demo_${id}.txt")

我知道 (A) 处的 JMS 消费者将在每次轮询时分出 JMS Transaction 并附加到线程。此外,(B)中的事务节点将在交换到达那里并附加到线程后分叉出 JPA 事务。

请在下面找到我的问题:

  1. 两个不同的事务可以附加到一个线程(如上面的那个)吗?
  2. 如果是,应该暂停哪一个?
  3. 上述路由的提交和回滚顺序应该是什么?

    注意:我没有从 Camel In Action 2nd Ed 书中找到任何明显的答案,所以请指导我

4

2 回答 2

1

下午好,

这是您其他问题的变体。

这:

from("jms:queue:IN_QUEUE)      //(A) Transactional Endpoint

端点已事务处理,这意味着您已将 JMS 组件标记为事务处理,并且 JMS 会话将由 JmsTransactionManager 管理。

.transacted("required")   //(B) TX Policy with PROPAGATION_REQUIRED and 
JPATxManager

这不应该是 JPA 事务管理器,而是 JTA 事务管理器(如 Arjuna)。与您的其他问题一样,您现在有一个用于读取消息的 JMS 本地事务,以及用于您的数据库访问的本地 JPA 事务会话。您希望 PlatformTransactionManager(一个 JTA 事务管理器)为您同步本地事务。

至于你的问题:

两个不同的事务可以附加到一个线程(如上面的那个)吗?

这真的没有任何意义。

如果是,应该暂停哪一个?

什么都不会被暂停。

上述路由的提交和回滚顺序应该是什么?

数据库读取不是事务性的,不需要提交。文件写入实际上会在 JTA 事务上下文关闭时发生。这留下了数据库写入。如果失败,则 DB 读取无关紧要,消息将被放回源目标,并且不会调用文件写入。

为各种事务管理器启用调试日志记录非常有帮助。

我可以用痛苦的细节继续讨论这个问题。这更适合burki。我认为你会非常感激这一点。非常微妙,而且经常发生。

   from("jms:queue:SRC_QUEUE")
     .transacted("required")
     .to("jms1:queue:DEST_QUEUE") 

如果两个端点被标记为已交易...但是...您没有“已交易”行会怎样?好吧,在消息侦听器上启动了 JMS 本地事务。这将在路由结束时提交。有两个独立的本地 JMS 事务。这些不是由 JTA 事务管理器同步的。

实际发生的是消息“get”的提交被调用。消息“put”没有实际提交。当 JMS 会话关闭时,将提交消息“put”。这是在 JMS 规范中,关闭连接会固有地提交任何事务。所以,因为这两个组件之间没有链接,所以“get”被提交,然后“put”会话被关闭。

这意味着如果消息“get”的提交和消息“put”的会话关闭之间存在中断,您可能会丢失消息。

那有意义吗?本地事务之间没有联系,因此 Camel 按顺序关闭它们,从提交“get”开始,然后调用“put”。

JTA 事务同步是关键。您仍然拥有本地事务资源(不是 XA),但可以在非常轻量级的 JTA 事务上下文中很好地管理它们。

   from("jms:queue:SRC_QUEUE")
     .transacted("required")
     .to("DB:transactedwrite")
     .to("jms1:queue:DEST_QUEUE") 

我懒得查找数据库插入的正确语法,但你明白了。在这种情况下,如果 JMS 'put' 失败,您可以获得重复的数据库插入。这不是“全有或全无”XA 事务。事务按顺序提交。如果中间的一个成功,那么下一个事务将失败,那么“get”将被回滚,并且您将获得重复直到失败点。

于 2020-03-10T20:21:16.927 回答
0

抱歉,我无法回答您的具体问题,但我可以提供一些有关您路线交易的具体信息。

您有 3 个具有不同事务“范围”的不同“系统”

  • 您从中消费的 JMS 代理
  • 一个您从中读取和写入的数据库,并为您配置了一个 JPA TxManager
  • 文件系统(根本没有事务)作为目标

首先,如果您想在 JMS 和数据库之间实现事务安全,您必须使用 XA 事务

然后,不清楚您是否期望JMS 使用者进行事务处理(因为transacted()在您的路由中),或者您是否真的配置了与本地 JMS 事务的 JMS 连接。我假设你真的消费了交易。

让我们谈谈没有路线 B​​的情况:

  • 您从经纪人处消费交易
  • Camel 通过你的路由处理消息
  • 当路由处理过程中发生任何错误时,消息不会在代理上提交,因此会重新传递到您的路由

由事务消费者打开的事务由 Camel 保持打开状态,直到路由成功处理

所以唯一明显的问题是数据库写入之后的错误,它触发了重新交付,并且数据库写入再次完成。可能写入不是幂等的,因此不能发生两次

因此,要解决这些问题,您要么必须使用 XA 事务,要么只需使用本地 JMS 事务并为上述“间隙”实现补偿逻辑。

另一方面,数据库事务没有任何好处,除非读写操作必须在事务中完成(但我怀疑两个单独的 bean 调用和一个 JMS 使用者的情况是否如此)。

于 2020-03-09T11:39:43.323 回答