引发异常后,如何处理SFTP入站适配器轮询中的剩余文件?

时间:2019-06-11 07:27:38

标签: java spring-integration spring-integration-sftp

我创建了一个集成流程来从SFTP服务器读取文件并进行处理。我意识到,一旦其中一个文件出现错误(引发异常),轮询将停止,并且其他任何文件都将在下一次轮询之前得到处理。如何避免这种情况,不将文件标记为已处理,然后处理该轮询中的其余文件?

我的配置非常简单。我使用的非事务轮询器每分钟触发一次,其中max-message-per-poll为1000。SftpStreamingInboundChannelAdapterSpec的{​​{1}}为10,并使用带有{{1}的复合文件列表过滤器}和max-fetch-size

SftpRegexPatternFileListFilter

阅读this answer之后,我尝试使用事务轮询器,如下所示:

SftpPersistentAcceptOnceFileListFilter

,但是结果是在文件处理失败之后,轮询将停止,所有已处理的消息都将回滚,并且剩余的消息将在下一次轮询之前才得到处理。我从该答案中了解到,每条消息将在单独的事务中进行处理。

到目前为止,我发现实现此目标的唯一方法是将处理代码包围在try / catch块中,以捕获所有异常,以避免中断轮询。在catch块中,我从复合文件列表过滤器中手动删除了@Bean public IntegrationFlow sftpInboundFlow(JdbcMetadataStore jdbcMetadataStore, DataSourceTransactionManager dataSourceTransactionManager) { return IntegrationFlows.from(sftpStreamingInboundChannelAdapterSpec(jdbcMetadataStore), sourcePollingChannelAdapterSpec -> configureEndpoint(sourcePollingChannelAdapterSpec, dataSourceTransactionManager)) .transform(new StreamTransformer()) .channel("processingChannel") .get(); } private SftpStreamingInboundChannelAdapterSpec sftpStreamingInboundChannelAdapterSpec(JdbcMetadataStore jdbcMetadataStore) { SftpStreamingInboundChannelAdapterSpec sftpStreamingInboundChannelAdapterSpec = Sftp.inboundStreamingAdapter(documentEnrollementSftpRemoteFileTemplate()) .filter(fileListFilter(jdbcMetadataStore)) .maxFetchSize(10) .remoteDirectory("/the-directory"); SftpStreamingMessageSource sftpStreamingMessageSource = sftpStreamingInboundChannelAdapterSpec.get(); sftpStreamingMessageSource.setFileInfoJson(false); return sftpStreamingInboundChannelAdapterSpec; } private void configureEndpoint(SourcePollingChannelAdapterSpec sourcePollingChannelAdapterSpec, DataSourceTransactionManager dataSourceTransactionManager) { PollerSpec pollerSpec = Pollers.cron(sftpProperties.getPollCronExpression()) .maxMessagesPerPoll(1000); sourcePollingChannelAdapterSpec.autoStartup(true) .poller(pollerSpec); } @Bean public CompositeFileListFilter<ChannelSftp.LsEntry> fileListFilter(JdbcMetadataStore jdbcMetadataStore) { String fileNameRegex = // get regex SftpRegexPatternFileListFilter sftpRegexPatternFileListFilter = new SftpRegexPatternFileListFilter(fileNameRegex); SftpPersistentAcceptOnceFileListFilter sftpPersistentAcceptOnceFileListFilter = new SftpPersistentAcceptOnceFileListFilter(jdbcMetadataStore, ""); CompositeFileListFilter<ChannelSftp.LsEntry> compositeFileListFilter = new CompositeFileListFilter<>(); compositeFileListFilter.addFilter(sftpRegexPatternFileListFilter); compositeFileListFilter.addFilter(sftpPersistentAcceptOnceFileListFilter); return compositeFileListFilter; } 。为此,我需要在PollerSpec pollerSpec = Pollers.cron(sftpProperties.getPollCronExpression()) .maxMessagesPerPoll(1000) .transactional(dataSourceTransactionManager); 提供的ChannelSftp.LsEntry中将属性fileInfoJson设置为false

我发现这种方法相当复杂,缺点是失败的文件和从过滤器中删除的文件将在之后而不是在下面的民意测验中立即进行重新处理。我希望有一个更直接的解决方案来解决我的问题。

1 个答案:

答案 0 :(得分:1)

尝试使用 try ... catch 解决方案。实际上,从该过程引发的异常会冒泡进入轮询器,并停止std::vector周围的当前循环:

maxMessagesPerPoll

private Runnable createPoller() { return () -> this.taskExecutor.execute(() -> { int count = 0; while (this.initialized && (this.maxMessagesPerPoll <= 0 || count < this.maxMessagesPerPoll)) { if (pollForMessage() == null) { break; } count++; } }); } 如下所示:

pollForMessage()

无论如何,仍然存在在单个轮询周期中将一条消息与其他消息隔离的方法。为此,您需要查看请求处理程序建议链,并使用private Message<?> pollForMessage() { try { return this.pollingTask.call(); } catch (Exception e) { if (e instanceof MessagingException) { throw (MessagingException) e; } else { Message<?> failedMessage = null; if (this.transactionSynchronizationFactory != null) { Object resource = TransactionSynchronizationManager.getResource(getResourceToBind()); if (resource instanceof IntegrationResourceHolder) { failedMessage = ((IntegrationResourceHolder) resource).getMessage(); } } throw new MessagingException(failedMessage, e); // NOSONAR (null failedMessage) } } finally { if (this.transactionSynchronizationFactory != null) { Object resource = getResourceToBind(); if (TransactionSynchronizationManager.hasResource(resource)) { TransactionSynchronizationManager.unbindResource(resource); } } } } https://docs.spring.io/spring-integration/docs/current/reference/html/#message-handler-advice-chain

研究解决方案

因此,您将其添加到下游的处理程序端点中,并在那儿捕获异常,并执行一些特定的错误处理,而不是将异常重新抛出给轮询器。