在管道的其余部分之前提交Spring-Integration事务

时间:2016-12-12 16:03:20

标签: java spring transactions spring-integration

所以,这是我目前的设置:

<int-amqp:inbound-channel-adapter channel="input-channel" queue-names="probni" message-converter="jsonMessageConverter"
                                  channel-transacted="true"
                                  transaction-manager="dataSourceTransactionManager"/>
<int:chain input-channel="input-channel" output-channel="inputc1">
    <int:service-activator ref="h1" method="handle" />
    <int:service-activator ref="h2" method="handle" />
    <int:service-activator ref="h3" method="handle" />
    <int:splitter  />
</int:chain>

<int:publish-subscribe-channel id="inputc1"/>

<int:claim-check-in input-channel="inputc1" output-channel="nullChannel" message-store="messageStore" order="1" />

<int:bridge input-channel="inputc1" output-channel="inputc2" order="2" />

<int:publish-subscribe-channel id="inputc2" task-executor="taskExecutor" />

<int-amqp:outbound-channel-adapter channel="inputc2" exchange-name="exch" amqp-template="rabbitTemplate" order="1" />
<int:service-activator input-channel="inputc2" output-channel="nullChannel"
                       expression="@messageStore.removeMessage(headers['id'])" order="2" />

并且图像是:

enter image description here

我需要的是事务(transaction-manager =&#34; dataSourceTransactionManager&#34;它应该是粉红色的部分)在任何消息被转发到int:brdige或int:bridge(蓝色)之前完全提交arrow - 所以bridge是边界组件,只有在jdbc已提交时才应实际转发消息。)

谢谢!

更新

以下是我需要这种设置的原因:

USE CASE:接收amqp消息,处理它并保存到db并将生成的amqp消息转发到管道中。消息不应丢失(既没有到达也没有消息,例如断电等)。 形成序列的多条消息可以到达具有相同组织的多个不同进程,如下所示。

我想如何解决它:

线程1:

  1. 从AMQP接收消息并开始交易
  2. 在一系列服务激活器中处理AMQP消息(可能每个激活器都会写入db,但在提交之前不会保存更改)
  3. 将传出的AMQP消息写入数据库(完全序列化) - 在提交之前不会保存
  4. COMMIT
  5. 确认收到的消息给AMQP
  6. 将传出消息转发到进程中的THREAD2(不是数据库指针,而是实际消息)
  7. 线程2:

    1. 从THREAD1
    2. 接收进程中的消息
    3. 尝试向AMQP发送消息
    4. 如果发送成功,请从THREAD1步骤3中保存的DB中删除传出的AMQP消息。
    5. THREAD3:

      1. 轮询DB以发送消息(来自THREAD1步骤3)(每10秒)
      2. 如果找到任何新消息,请将其标记为在下次轮询中发送(同时THREAD2可以删除此消息)
      3. 如果在第二轮询(20秒后)消息仍然存在 - 意味着THREAD2无法发送它,所以我们在这里产生新线程,它将执行与THREAD2相同的任务。
      4. 更新2

        试过这个设置,但有一些问题:

        <int:transaction-synchronization-factory id="transactionSynchronizationFactory">
            <int:after-commit expression="payload" channel="committed-channel" />
        </int:transaction-synchronization-factory>
        
        <int-amqp:inbound-channel-adapter channel="input-channel" queue-names="probni" message-converter="jsonMessageConverter"
                                          channel-transacted="true"
                                          transaction-manager="dataSourceTransactionManager" advice-chain="amqpMethodInterceptor"/>
        

        @Component
        public class AmqpMethodInterceptor implements MethodInterceptor {
        
            private TransactionSynchronizationFactory factory;
        
            public AmqpMethodInterceptor(TransactionSynchronizationFactory factory){
                this.factory = factory;
            }
        
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
        
        
                if (TransactionSynchronizationManager.isActualTransactionActive()) {
                    TransactionSynchronization synchronization = factory.create("123");
                    TransactionSynchronizationManager.registerSynchronization(synchronization);
                }
        
                Object result = invocation.proceed();
        
                return result;
            }
        }
        

        调用after-commit,但此时此消息为null,因此似乎没有任何内容可以转发到committed-channel。知道如何做这个部分吗?

1 个答案:

答案 0 :(得分:1)

只有使用TransactionSynchronization才能实现。

这座桥真的是边界&#34; TX,但这里的提交确实发生在send()方法之后。仅仅因为你的下一个频道有一个executor,所以,当前的事务线程无需做更多事情,只在发送之后执行提交,而不是之前。

为了实现目标,您应该通过MethodInterceptor实施<int-amqp:inbound-channel-adapter>建议以注入advice-chain。并尝试将DefaultTransactionSynchronizationFactory的逻辑与ExpressionEvaluatingTransactionSynchronizationProcessor一起使用,您可以在其中向afterCommitChannel发送消息。

Advice中的代码应使用此模板:

if (TransactionSynchronizationManager.isActualTransactionActive()) {
                TransactionSynchronization synchronization = this.transactionSynchronizationFactory.create(key);
                TransactionSynchronizationManager.registerSynchronization(synchronization);
}

其中key可以是任何唯一的对象,以区分与TX同步的资源。

<强>更新

  

但此时此消息为空,因此我似乎无需转发到已提交的通道。

这是真的,因为你以不寻常的方式使用TransactionSynchronizationFactory

好吧,不管怎样,让我们​​试着欺骗它,因为对我来说你是正确的。

factory.create("123");执行此操作:

    DefaultTransactionalResourceSynchronization synchronization = new DefaultTransactionalResourceSynchronization(key);
    TransactionSynchronizationManager.bindResource(key, synchronization.getResourceHolder());
    return synchronization;

主要有{​​{1}}。我的想法在下游的某个地方,在TX结束之前,这样做:

TransactionSynchronizationManager.bindResource()

我认为即便可以做到这一点:

IntegrationResourceHolder holder =
                    (IntegrationResourceHolder) TransactionSynchronizationManager.getResource("123");
holder.setMessage(message);

作为退出TX之前的最后一个端点。