如何设计全局分布式事务(无数据库)? JTA可以用于无数据库事务吗?

时间:2012-03-20 22:00:56

标签: java jta distributed-transactions

我认为这是一个相当普遍的问题:如何将我的业务逻辑放在分布式系统环境中的全局事务中?举个例子,我有一个包含几个子任务的TaskA:

TaskA {subtask1,subtask2,subtask3 ...}

这些子任务中的每一个都可以在本地机器或远程机器上执行,我希望TaskA通过事务以原子方式(成功或失败)执行。每个子任务都有一个回滚函数,一旦TaskA认为操作失败(因为其中一个子任务失败),它就会调用子任务的每个回滚函数。否则,TaskA会提交整个事务。

为此,我按照“审计试用”事务模式来记录每个子任务,因此TaskA可以知道子任务的操作结果,然后决定回滚或提交。这听起来很简单,但是,困难的部分是如何将每个子任务与全局事务相关联?

当TaskA开始时,它会启动一个关于哪个子任务什么都不知道的全局事务。为了使子任务意识到它,我必须将事务上下文传递给每个子任务调用。这真是太可怕了!我的子任务可以在新线程中执行,也可以通过AMQP代理发送消息在远程执行,很难巩固上下文传播的方式。

我做过一些研究,比如“交易模式 - 四种交易相关模式的集合”,“异步消息传递环境中的已检查交易”,这些都没有解决我的问题。他们要么没有实际的例子,要么没有解决上下文传播问题。

我想知道人们如何解决这个问题?因为这种交易必须在企业软件中很常见。

X / Open XA只是解决方案吗? JTA可以在这里提供帮助(我没有关注JTA,因为它与数据库事务有关,我使用Spring,我不想在我的软件中涉及另一个Java EE应用服务器)。

有些专家可以与我分享一些想法吗?谢谢。

结论

Arjan和Martin给出了非常好的答案,谢谢。 最后我没有这样做。经过更多的研究,我选择了另一种模式“ CheckPoint 1

根据我的要求,我发现我的“审核试用交易模式”的意图是知道操作已进行到哪个级别,如果失败,我可以在重新加载某些上下文后在失败的地点重新启动它。实际上这不是事务,它在失败后没有回滚其他成功的步骤。这是CheckPoint模式的精髓。 然而,研究分布式交易的东西让我学到了很多有趣的东西。除了Arjan和Martin提到的。我还建议人们深入研究这个领域,看看CORBA这是一个众所周知的分布式系统协议。

3 个答案:

答案 0 :(得分:11)

你是对的,由于JTA API提供的XA事务管理器,你需要两阶段提交支持。

据我所知,Spring本身并未提供事务管理器实现。 JtaTransactionManager仅委托通常由JavaEE实现提供的现有实现。

因此,您必须将JTA实现插入Spring才能完成有效的工作。以下是一些建议:

然后,您必须实现资源管理器以支持两阶段提交。在JavaEE世界中,它包含一个打包为RAR存档的资源适配器。根据资源类型,以下方面是阅读/实施:

作为示例,我建议您查看经典的“带文件交易”问题的实现:

答案 1 :(得分:7)

如果你想编写自己的事务资源,你确实需要实现一个XAResource并让它加入一个正在进行的事务,处理准备和提交来自事务管理器的请求等。

数据源是最知名的交易资源,但如上所述,它们不是唯一的。您已经提到了JMS提供程序。各种缓存解决方案(例如Infinispan)也是交易资源。

实现XAResources并使用JTA API的低级部分和更低级别的JTS(Java事务服务)对于胆小者来说不是一项任务。 API可能过时,整个过程几乎没有记录。

原因是创建自己的事务资源的常规企业应用程序极为罕见。事务性的整个要点是为外部观察者做一个原子的动作。

在绝大多数情况下可观察到意味着动作的效果存在于数据库中。几乎每个数据源都是事务性的,因此完全覆盖了用例。

另一个可观察到的影响是消息是否已发送,现有消息传递解决方案也完全覆盖了这些消息。

最后,更新内存映射中的(群集范围)是另一个可观察到的效果,主要缓存提供程序也会这样做。

在使用外部企业信息系统(EIS)进行操作时,对事务影响的需求仍然存在,并且根据经验,此类系统的供应商提供了事务感知连接器。

剩下的用例颤抖是如此之小,以至于显然没有人真的费心去写太多。有一些博客涉及一些基础知识,但很多将留给你自己的实验。

如果您真的绝对需要走这条路,并且如果现有的交易资源无法满足您的需求,请尝试自行验证。

答案 2 :(得分:0)

您可以执行以下操作(类似于您的检查点策略):

  1. TaskA在(本地)JTA事务中执行,并在委派给子任务之前尝试保留必要的资源

  2. 子任务调用是通过JMS / XA完成的:如果步骤1失败,则不会触发任何子任务,如果步骤1提交,则将在每个子任务中接收JMS调用

  3. 子任务(重新)尝试使用JMS最大重新传递限制集处理其调用消息(请参阅有关如何执行此操作的JMS供应商文档)

  4. 为3中的任何失败配置“死信队列”。

  5. 这是有效的,假设:

    步骤3中的重试有意义

    - 死信队列中的消息需要进行一些人工干预

    如果这是不可接受的,那么还有TCC:http://www.atomikos.com/Main/DownloadWhitepapers?article=TccForRestApi.pdf - 这可以被视为REST服务的“预留”模式。

    希望这有帮助

    http://www.atomikos.com