Spring与ActiveMQ集成的事务边界和使用Message Store支持的通道

时间:2015-06-29 17:42:49

标签: java spring transactions spring-integration

我正在使用spring-integration和ActiveMQ来集成主服务器和几个“客户端”应用程序。

主要目标是使用嵌入式ActiveMQ代理开发小型客户端,嵌入式代理由此客户端应用程序启动 以便从同一站点上的多个第三方应用程序接收JMS消息。客户端应用程序通过JMS连接与服务器连接 主要安装ActiveMQ服务器(这已经存在)。 我需要为客户端提供离线功能,这意味着如果客户端断开连接 应将从第三个客户端收到的消息存储在同一站点上,并在服务器再次联机时发送它们。 当没有涉及事务性时,所有这些似乎都正常工作,但我对spring-integration中配置的通道的事务边界有疑问。

主要流程是:

同一网站上的第三个客户 - >我们的网站应用程序客户 - >重新格式化消息并在messageTranslator - >上添加信息存储在remoteChannel - >发送到主服务器

这是我的网站客户端配置:

客户端应用中的嵌入式ActiveMQ代理:

<bean id="broker1" class="org.apache.activemq.xbean.BrokerFactoryBean">
    <property name="config" value="classpath:activemq/activemq.xml" />
    <property name="start" value="true" />
</bean>

连接工厂; remoteConnectionFactory是与主服务器和队列定义的连接:

<!-- Local queue definitions  -->
<bean id="localConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
    <property name="targetConnectionFactory">
        <bean class="org.apache.activemq.ActiveMQConnectionFactory">
            <property name="brokerURL" value="vm://localhost"/>
        </bean>
    </property>
    <property name="sessionCacheSize" value="10"/>
</bean>
<bean id="localEventQueue" class="org.apache.activemq.command.ActiveMQQueue"  >
    <constructor-arg value="event.client"/>
</bean>

<!-- Remote(Server) queue definitions  -->
<bean id="remoteConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
    <property name="targetConnectionFactory">
        <bean class="org.apache.activemq.ActiveMQConnectionFactory">
            <property name="brokerURL" value="tcp://192.168.0.125:62626"/>
        </bean>
    </property>
    <property name="sessionCacheSize" value="10"/>
</bean>
<bean id="remoteQueue" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="remote.event"/>
</bean>

本地流程,在同一个远程站点上:

<!-- local process -->
<int:channel id="transformChannel" >
    <int:queue />
</int:channel>

<int-jms:message-driven-channel-adapter
        id="jmsEventsFromClient"
         connection-factory="localConnectionFactory"
         destination="localEventQueue"
         channel="transformChannel"
        concurrent-consumers="4"
        auto-startup="true"/>

<!-- Transforms the message from clients to a standard messages that server knows-->
<int:service-activator input-channel="transformChannel"
                       ref="messageTranslator"
                       output-channel="remoteChannel"  >
    <int:poller  fixed-delay="500">
    </int:poller>
</int:service-activator>


<int:transaction-synchronization-factory id="syncFactory">
    <int:after-commit expression="@store.removeFromIdCache(headers.id.toString())" />
    <int:after-rollback expression="@store.removeFromIdCache(headers.id.toString())"/>
</int:transaction-synchronization-factory>

<int:channel id="remoteChannel" >
     <int:queue message-store="store" />
</int:channel>

<!-- local file database with HSQL store of the remotechannel -->
<bean id="store" class="org.springframework.integration.jdbc.store.JdbcChannelMessageStore">
    <property name="dataSource" ref="dataSource"/>
    <property name="channelMessageStoreQueryProvider">
        <bean id="queryProvider" class="org.springframework.integration.jdbc.store.channel.HsqlChannelMessageStoreQueryProvider"/>
    </property>
    <property name="usingIdCache" value="true"/>
</bean>

发送到远程队列:

<!-- the message is send to the remote server -->
<int-jms:outbound-channel-adapter id="remoteJms"
                                  connection-factory="remoteConnectionFactory"
                                  destination="remoteQueue"
                                  channel="remoteChannel"
                                  session-transacted="true"
                                  auto-startup="true">

    <int:poller  fixed-delay="500">
        <int:transactional propagation="REQUIRED"
                           isolation="DEFAULT"
                           synchronization-factory="syncFactory"
                           transaction-manager="transactionManager"/>
    </int:poller>

</int-jms:outbound-channel-adapter>

当没有配置JTA事务管理器且remoteJms不是session-transacted时,一切正常,即使我模拟离线情况,消息也正确地存储在store上,当与服务器的连接再次生效(remoteJms)时,消息将正确发送。 但是,当配置JTA事务管理器(atomikos)并且ActiveMQ主服务器关闭时,消息不会存储在store上。我认为只要remoteJms中的标记收到消息就会开始新的交易,但这种情况不会发生,它从jmsEventsFromClientjmsOut的整个过程看起来像是一个单个事务并因remoteJms已关闭而失败。

我想要的是有2个交易,第一个应该在eventQueue收到消息时开始,当消息留在remoteChannel时结束,第二个应该在邮件是从remoteChannel获取的,并将其保留在remoteJms上。 感谢您的意见和帮助。

编辑
仅当远程服务器关闭时才会发生这种情况,并且消息已从store中删除,但这是我不想要的。

添加日志错误。

2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'dataSource': getConnection ( null )...
2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'dataSource': init...
2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 7472616E73616374696F6E4D616E6167657230303038303030303430:7472616E73616374696F6E4D616E616765723830 ) for transaction transactionManager0008000040
2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 7472616E73616374696F6E4D616E6167657230303038303030303430:7472616E73616374696F6E4D616E616765723830 , XAResource.TMNOFLAGS ) on resource dataSource represented by XAResource instance org.hsqldb.jdbc.pool.JDBCXAResource@55f823a3
2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@c85d8b3 ) for transaction transactionManager0008000040
2015-06-29 16:39:16.859  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for org.hsqldb.jdbc.pool.JDBCXAConnectionWrapper@731d1112: calling prepareStatement(SELECT COUNT(MESSAGE_ID) from INT_CHANNEL_MESSAGE where GROUP_KEY=? and REGION=?)...
hi message: una cadena 
2015-06-29 16:39:17.820  INFO 4982 --- [ask-scheduler-9] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'dataSource': getConnection ( null )...
2015-06-29 16:39:17.820  INFO 4982 --- [ask-scheduler-9] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'dataSource': init...
2015-06-29 16:39:25.093  WARN 4982 --- [0.1:57663@61616] o.a.a.b.TransportConnection.Transport    : Transport Connection to: tcp://192.168.0.125:57663 failed: java.io.EOFException
2015-06-29 16:39:27.145  WARN 4982 --- [     Atomikos:1] c.a.icatch.imp.ActiveStateHandler        : Timeout/setRollbackOnly of ACTIVE coordinator !
2015-06-29 16:39:47.821  WARN 4982 --- [ask-scheduler-9] com.atomikos.jdbc.AtomikosSQLException   : Connection pool exhausted - try increasing 'maxPoolSize' and/or 'borrowConnectionTimeout' on the DataSourceBean.
2015-06-29 16:39:47.822  INFO 4982 --- [ask-scheduler-4] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@c85d8b3 ) for transaction transactionManager0008000040
2015-06-29 16:39:47.822  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for org.hsqldb.jdbc.pool.JDBCXAConnectionWrapper@731d1112: calling prepareStatement(SELECT COUNT(MESSAGE_ID) from INT_CHANNEL_MESSAGE where GROUP_KEY=? and REGION=?)...
2015-06-29 16:39:47.823  INFO 4982 --- [ask-scheduler-4] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@c85d8b3 ) for transaction transactionManager0008000040
2015-06-29 16:39:47.823  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for org.hsqldb.jdbc.pool.JDBCXAConnectionWrapper@731d1112: calling prepareStatement(SELECT INT_CHANNEL_MESSAGE.MESSAGE_ID, INT_CHANNEL_MESSAGE.MESSAGE_BYTES from INT_CHANNEL_MESSAGE where INT_CHANNEL_MESSAGE.GROUP_KEY = ? and INT_CHANNEL_MESSAGE.REGION = ? order by CREATED_DATE, MESSAGE_SEQUENCE LIMIT 1)...
2015-06-29 16:39:47.823  INFO 4982 --- [ask-scheduler-4] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for org.hsqldb.jdbc.pool.JDBCXAConnectionWrapper@731d1112: close()...
2015-06-29 16:39:47.823  INFO 4982 --- [ask-scheduler-4] c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 7472616E73616374696F6E4D616E6167657230303038303030303430:7472616E73616374696F6E4D616E616765723830 , XAResource.TMSUCCESS ) on resource dataSource represented by XAResource instance org.hsqldb.jdbc.pool.JDBCXAResource@55f823a3
2015-06-29 16:39:47.823  INFO 4982 --- [ask-scheduler-4] c.a.icatch.imp.CompositeTransactionImp   : commit() done (by application) of transaction transactionManager0008000040
2015-06-29 16:39:47.824  INFO 4982 --- [ask-scheduler-4] c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 7472616E73616374696F6E4D616E6167657230303038303030303430:7472616E73616374696F6E4D616E616765723830 ) returning OK on resource dataSource represented by XAResource instance org.hsqldb.jdbc.pool.JDBCXAResource@55f823a3
2015-06-29 16:39:47.824  INFO 4982 --- [ask-scheduler-4] c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 7472616E73616374696F6E4D616E6167657230303038303030303430:7472616E73616374696F6E4D616E616765723830 ) on resource dataSource represented by XAResource instance org.hsqldb.jdbc.pool.JDBCXAResource@55f823a3
2015-06-29 16:39:47.825 ERROR 4982 --- [ask-scheduler-4] o.s.integration.handler.LoggingHandler   : org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1024)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:521)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy85.call(Unknown Source)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:298)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:49)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:292)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.transaction.RollbackException: Prepare: NO vote
    at com.atomikos.icatch.jta.TransactionImp.rethrowAsJtaRollbackException(TransactionImp.java:66)
    at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:206)
    at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:436)
    at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:177)
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1021)
    ... 22 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
    at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.java:231)
    at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681)
    at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970)
    at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82)
    at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336)
    at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:190)
    ... 25 more

2015-06-29 16:39:47.825 ERROR 4982 --- [ask-scheduler-9] o.s.integration.handler.LoggingHandler   : org.springframework.messaging.MessageDeliveryException: failed to send Message to channel 'remoteChannel'; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.atomikos.jdbc.AtomikosSQLException: Connection pool exhausted - try increasing 'maxPoolSize' and/or 'borrowConnectionTimeout' on the DataSourceBean.
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:292)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:95)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:248)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:171)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:119)
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:105)
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
    at org.springframework.integration.endpoint.PollingConsumer.handleMessage(PollingConsumer.java:74)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:219)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.access$000(AbstractPollingEndpoint.java:55)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:149)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:146)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:298)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:49)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:292)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.atomikos.jdbc.AtomikosSQLException: Connection pool exhausted - try increasing 'maxPoolSize' and/or 'borrowConnectionTimeout' on the DataSourceBean.
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:630)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:909)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:970)
    at org.springframework.integration.jdbc.store.JdbcChannelMessageStore.addMessageToGroup(JdbcChannelMessageStore.java:422)
    at org.springframework.integration.store.MessageGroupQueue.doOffer(MessageGroupQueue.java:329)
    at org.springframework.integration.store.MessageGroupQueue.put(MessageGroupQueue.java:274)
    at org.springframework.integration.store.MessageGroupQueue.put(MessageGroupQueue.java:48)
    at org.springframework.integration.channel.QueueChannel.doSend(QueueChannel.java:92)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277)
    ... 28 more
Caused by: com.atomikos.jdbc.AtomikosSQLException: Connection pool exhausted - try increasing 'maxPoolSize' and/or 'borrowConnectionTimeout' on the DataSourceBean.
    at com.atomikos.jdbc.AtomikosSQLException.throwAtomikosSQLException(AtomikosSQLException.java:46)
    at com.atomikos.jdbc.AbstractDataSourceBean.throwAtomikosSQLException(AbstractDataSourceBean.java:90)
    at com.atomikos.jdbc.AbstractDataSourceBean.throwAtomikosSQLException(AbstractDataSourceBean.java:85)
    at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:347)
    at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:394)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    ... 37 more

1 个答案:

答案 0 :(得分:0)

我想我已经明白了 - 尝试在从商店支持的receive-timeout="0"拉出的轮询器上设置QueueChannel