如何确保 SFTP 会话始终在 spring-batch 结束时关闭

Posted

技术标签:

【中文标题】如何确保 SFTP 会话始终在 spring-batch 结束时关闭【英文标题】:How to make sure SFTP session always close at the end of the spring-batch 【发布时间】:2019-12-17 11:16:28 【问题描述】:

我的应用程序基于 spring-boot 2.1.6,配备了 spring-batch(块方法)和 spring-integration 来处理 SFTP。

高级功能是从数据库中获取数据,生成文本文件,然后通过 SFTP 发送,此任务每 30 分钟运行一次。

此应用程序已经在生产环境中运行了一段时间,但如果我看到日志,则会出现关于 ssh_msg_disconnect 11 idle connection 的错误。它会一直保持这种状态,直到我重新启动应用程序。

以下是我的应用程序代码:

SftpConfig.java

@Configuration
public class SftpConfig 

    @Autowired
    ApplicationProperties applicationProperties;

    @Bean
    public SessionFactory<LsEntry> sftpSessionFactory() 
        final DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(applicationProperties.getSftp().getHost());
        factory.setUser(applicationProperties.getSftp().getUser());
        factory.setPassword(applicationProperties.getSftp().getPass());
        factory.setAllowUnknownKeys(true);

        return new CachingSessionFactory<>(factory);
    

    @Bean
    @ServiceActivator(inputChannel = "toSftpChannel", adviceChain = "retryAdvice")
    public MessageHandler handler() 
        final SftpMessageHandler handler = new SftpMessageHandler(this.sftpSessionFactory());
        handler.setRemoteDirectoryExpression(new LiteralExpression(applicationProperties.getSftp().getPath()));
        handler.setFileNameGenerator((final Message<?> message) -> 
            if (message.getPayload() instanceof File) 
                return ((File) message.getPayload()).getName();
             else 
                throw new IllegalArgumentException("File expected as payload.");
            
        );

        return handler;
    

    @Bean
    public RequestHandlerRetryAdvice retryAdvice() 
        final RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
        final RetryTemplate retryTemplate = new RetryTemplate();
        final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(NumberConstants.FIVE);
        retryTemplate.setRetryPolicy(retryPolicy);
        advice.setRetryTemplate(retryTemplate);

        return advice;
    

    @MessagingGateway
    public interface UploadGateway 

        @Gateway(requestChannel = "toSftpChannel")
        void upload(File file);
    


发送文件到 sftp 的步骤


@Autowired
UploadGateway uploadGateway;

private boolean uploadToSharedFolderSuccess(final PaymentStatus paymentStatus, final String strLocalTmpPath) 
        try 
            final File fileLocalTmpFullPath = new File(strLocalTmpPath);
            uploadGateway.upload(fileLocalTmpFullPath);
         catch (final Exception e) 
            paymentStatus.setStatus(ProcessStatus.ERROR.toString());
            paymentStatus.setRemark(StringUtil.appendIfNotEmpty(paymentStatus.getRemark(),
                    "Error during upload to shared folder - " + e.getMessage()));
        
        return !StringUtils.equalsIgnoreCase(ProcessStatus.ERROR.toString(), paymentStatus.getStatus());
    

从错误中,我知道似乎我打开了太多连接。但我不确定如何检查连接是否在 spring-batch 的每一端都关闭。

【问题讨论】:

基本上@ServiceActivator 是一个被动组件,它按需从SessionFactory 获取会话。无论如何,下一次通话都应该带来一个新的会话,因为您不使用 isSharedSession 标志。我建议将此标志设置为真。如需参考,请查看属性文档docs.spring.io/spring-integration/docs/5.2.0.M3/reference/html/… 如果您希望会话在使用后物理关闭,请不要使用CachingSessionFactory @Dhiren,isSharedSession 标志在那里,并在初始化会话工厂期间设置为 true .. 关于按需的事情,是否有任何文章可以支持该声明? @Gary Russell,那我应该用什么?有什么建议吗? 如果你想为每个调用分开会话然后使用 DefaultSessionFactory 否则 CachingSessionFactory。取决于您的用例。 【参考方案1】:

如果您不将会话工厂包装在 CachingSessionFactory 中,则会话将在每次使用后关闭。

@Bean
public DefaultSftpSessionFactory sftpSessionFactory() 
    final DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
    factory.setHost(applicationProperties.getSftp().getHost());
    factory.setUser(applicationProperties.getSftp().getUser());
    factory.setPassword(applicationProperties.getSftp().getPass());
    factory.setAllowUnknownKeys(true);

    return factory;

【讨论】:

以上是关于如何确保 SFTP 会话始终在 spring-batch 结束时关闭的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 libssh 和 SFTP 在 C/C++ 中复制文件

php 如何通过连接sftp并下载sftp服务器指定目录下的所有文件到本地?

使用 paramiko 防止 SFTP/SSH 会话超时

如何在中间件中使用会话来始终检查用户是不是登录

WinSCP:当前 SFTP-3 会话不支持您请求的命令。可以打开单独的 shell 会话来处理命令

SSH - 一种无需打开单独的 SFTP 会话即可传输文件的方法?