我正在4个不同的服务器上运行4个基于Spring Boot Integration的应用程序实例。 该过程是:
我正在寻找一种无阻塞且安全的解决方案来处理这些文件。
用例:
我已经构建了这个Spring Integration XML配置文件(它包含带有共享H2数据库的JDBC元数据存储):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<int:poller default="true" fixed-rate="1000"/>
<int:channel id="inputFilesChannel">
<int:queue/>
</int:channel>
<!-- Input -->
<int-file:inbound-channel-adapter
id="inputFilesAdapter"
channel="inputFilesChannel"
directory="file:${input.files.path}"
ignore-hidden="true"
comparator="lastModifiedFileComparator"
filter="compositeFilter">
<int:poller fixed-rate="10000" max-messages-per-poll="1" task-executor="taskExecutor"/>
</int-file:inbound-channel-adapter>
<task:executor id="taskExecutor" pool-size="1"/>
<!-- Metadatastore -->
<bean id="jdbcDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:h2:file:${database.path}/shared;AUTO_SERVER=TRUE;AUTO_RECONNECT=TRUE;MVCC=TRUE"/>
<property name="driverClassName" value="org.h2.Driver"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="maxIdle" value="4"/>
</bean>
<bean id="jdbcMetadataStore" class="org.springframework.integration.jdbc.metadata.JdbcMetadataStore">
<constructor-arg ref="jdbcDataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="jdbcDataSource"/>
</bean>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.FileSystemPersistentAcceptOnceFileListFilter">
<constructor-arg index="0" ref="jdbcMetadataStore"/>
<constructor-arg index="1" value="files"/>
</bean>
</list>
</constructor-arg>
</bean>
<!-- Workflow -->
<int:chain input-channel="inputFilesChannel" output-channel="outputFilesChannel">
<int:service-activator ref="fileActivator" method="fileRead"/>
<int:service-activator ref="fileActivator" method="fileProcess"/>
<int:service-activator ref="fileActivator" method="fileAudit"/>
</int:chain>
<bean id="lastModifiedFileComparator" class="org.apache.commons.io.comparator.LastModifiedFileComparator"/>
<int-file:outbound-channel-adapter
id="outputFilesChannel"
directory="file:${output.files.path}"
filename-generator-expression ="payload.name">
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpressionString" value="headers[file_originalFile].delete()"/>
</bean>
</int-file:request-handler-advice-chain>
</int-file:outbound-channel-adapter>
</beans>
问题:
对于多个文件,当成功处理1个文件时,事务将提交元数据存储中其他现有的文件(表INT_METADATA_STORE
)。因此,如果应用程序重新启动,其他文件将永远不会被处理
(如果在处理第一个文件时应用崩溃,则可以正常工作)。
似乎它仅适用于读取文件,而不适用于处理集成链中的文件...如何按文件管理JVM崩溃文件上的回滚事务?
我们非常感谢您的帮助。这会让我发疯:(
谢谢!
编辑/注释:
我已经用Artem Bilan的答案更新了配置。并删除transactional
块中的poller
块:实例之间发生事务冲突(丑陋的表锁异常)。尽管行为是相同的。
我在poller
块(相同的行为)中未成功测试此配置:
<int:advice-chain>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="file*" timeout="30000" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
</int:advice-chain>
也许基于Idempotent Receiver Enterprise Integration Pattern的解决方案可能有效。但是我没能配置它...我找不到精确的文档。
答案 0 :(得分:1)
您不应使用PseudoTransactionManager
,而应使用DataSourceTransactionManager
。
由于您使用JdbcMetadataStore
,它将参与事务,并且如果下游流失败,则元数据存储中的条目也将被回滚。
答案 1 :(得分:1)
好的。我找到了可行的解决方案。也许不是最干净的,但它可以工作:
ActivatedRoute
已激活inbound-channel-adapter
选项,以允许重新轮询以前可以忽略的文件(如果该过程已由另一个实例开始)。因此,如果另一个实例崩溃了,就可以对该文件进行轮询并再次对其进行处理,而无需为此实例重新启动。scan-each-poll
设置为defaultAutoCommit
。false
,因为当一个文件被成功处理时,它会聚合元数据存储中的所有读取文件。我没有设法在上下文中使用它... 我通过过滤器和事务同步在表达式中写下了自己的条件和动作。
FileSystemPersistentAcceptOnceFileListFilter
欢迎任何改进或其他解决方案。