Spring Integration中无法使RequestHandlerRetryAdvice与Ftp.outboundGateway一起使用

时间:2018-09-25 12:26:48

标签: spring-boot spring-integration spring-integration-dsl

我的情况类似于this SO question中描述的情况。区别在于我不使用WebFlux.outboundGateway,而是使用我在其上调用Ftp.outboundGateway的{​​{1}},常见的问题是我无法获得已定义的{{1} }。

配置看起来像这样(简化到相关部分):

AbstractRemoteFileOutboundGateway.Command.GET
RequestHandlerRetryAdvice
@RestController
@RequestMapping( value = "/somepath" )
public class DownloadController
{
   private DownloadGateway downloadGateway;

   public DownloadController( DownloadGateway downloadGateway )
   {
      this.downloadGateway = downloadGateway;
   }

   @PostMapping( "/downloads" )
   public void download( @RequestParam( "filename" ) String filename )
   {
      Map<String, Object> headers = new HashMap<>();

      downloadGateway.triggerDownload( filename, headers );
   }
}    
@MessagingGateway
public interface DownloadGateway
{
   @Gateway( requestChannel = "downloadFiles.input" )
   void triggerDownload( Object value, Map<String, Object> headers );
}
@Configuration
@EnableIntegration
public class FtpDefinition
{
   private FtpProperties ftpProperties;

   public FtpDefinition( FtpProperties ftpProperties )
   {
      this.ftpProperties = ftpProperties;
   }

   @Bean
   public DirectChannel gatewayDownloadsOutputChannel()
   {
      return new DirectChannel();
   }

   @Bean
   public IntegrationFlow downloadFiles( RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile )
   {
      return f -> f.handle( getRemoteFile, getRetryAdvice() )
                   .channel( "gatewayDownloadsOutputChannel" );
   }

   private Consumer<GenericEndpointSpec<AbstractRemoteFileOutboundGateway<FTPFile>>> getRetryAdvice()
   {
      return e -> e.advice( ( (Supplier<RequestHandlerRetryAdvice>) () -> {
         RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
         advice.setRetryTemplate( getRetryTemplate() );
         return advice;
      } ).get() );
   }

   private RetryTemplate getRetryTemplate()
   {
      RetryTemplate result = new RetryTemplate();

      FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
      backOffPolicy.setBackOffPeriod( 5000 );

      result.setBackOffPolicy( backOffPolicy );
      return result;
   }

   @Bean
   public RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile( SessionFactory sessionFactory )
   {
      return 
         Ftp.outboundGateway( sessionFactory,
                              AbstractRemoteFileOutboundGateway.Command.GET,
                              "payload" )
            .fileExistsMode( FileExistsMode.REPLACE )
            .localDirectoryExpression( "'" + ftpProperties.getLocalDir() + "'" )
            .autoCreateLocalDirectory( true );
   }

   @Bean
   public SessionFactory<FTPFile> ftpSessionFactory()
   {
      DefaultFtpSessionFactory sessionFactory = new DefaultFtpSessionFactory();
      sessionFactory.setHost( ftpProperties.getServers().get( 0 ).getHost() );
      sessionFactory.setPort( ftpProperties.getServers().get( 0 ).getPort() );
      sessionFactory.setUsername( ftpProperties.getServers().get( 0 ).getUser() );
      sessionFactory.setPassword( ftpProperties.getServers().get( 0 ).getPassword() );
      return sessionFactory;
   }
}

Controller主要用于测试目的,在实际的实现中有一个轮询器。我的@SpringBootApplication @EnableIntegration @IntegrationComponentScan public class FtpTestApplication { public static void main(String[] args) { SpringApplication.run( FtpTestApplication.class, args ); } } 拥有服务器列表,因为在实际的实现中,我使用@Configuration @PropertySource( "classpath:ftp.properties" ) @ConfigurationProperties( prefix = "ftp" ) @Data public class FtpProperties { @NotNull private String localDir; @NotNull private List<Server> servers; @Data public static class Server { @NotNull private String host; @NotNull private int port; @NotNull private String user; @NotNull private String password; } } 根据一些参数选择一个实例。

根据Gary Russell's comment,我希望重试失败的下载。但是,如果我中断了下载服务器端(通过在FileZilla实例中发出“踢用户”),我将立即获得堆栈跟踪,而不会重试:

FtpProperties

我还需要上传使用DelegatingSessionFactory的文件。在这种情况下,并且使用相同的org.apache.commons.net.ftp.FTPConnectionClosedException: FTP response 421 received. Server closed connection. [...] ,如果我中断了一个上传服务器端,Spring Integration将再执行两次尝试,每次延迟5s,然后再记录Ftp.outboundAdapter,所有操作均符合预期。 / p>

我尝试调试一下,发现在第一次尝试通过RetryTemplate上传之前,java.net.SocketException: Connection reset上的一个断点被击中。但是当通过Ftp.outboundAdapter下载时,该断点是从不命中。

我的配置是否存在问题,有人可以让RequestHandlerRetryAdvice.doInvoke()Ftp.outboundGateway / RequestHandlerRetryAdvice一起使用吗?

1 个答案:

答案 0 :(得分:1)

很抱歉延迟;我们这周在SpringOne平台上。

问题是由于网关规范是一个bean的事实-网关最终在应用建议之前被初始化。

我这样更改了您的代码...

@Bean
public IntegrationFlow downloadFiles(SessionFactory<FTPFile> sessionFactory) {
    return f -> f.handle(getRemoteFile(sessionFactory), getRetryAdvice())
            .channel("gatewayDownloadsOutputChannel");
}

...

private RemoteFileOutboundGatewaySpec<FTPFile, FtpOutboundGatewaySpec> getRemoteFile(SessionFactory<FTPFile> sessionFactory) {
    return Ftp.outboundGateway(sessionFactory,
            AbstractRemoteFileOutboundGateway.Command.GET,
            "payload")
            .fileExistsMode(FileExistsMode.REPLACE)
            .localDirectoryExpression("'/tmp'")
            .autoCreateLocalDirectory(true);
}

...而且有效。

通常最好不要直接处理Specs,而直接将它们内联到流定义中...

@Bean
public IntegrationFlow downloadFiles(SessionFactory<FTPFile> sessionFactory) {
    return f -> f.handle(Ftp.outboundGateway(sessionFactory,
            AbstractRemoteFileOutboundGateway.Command.GET,
            "payload")
            .fileExistsMode(FileExistsMode.REPLACE)
            .localDirectoryExpression("'/tmp'")
            .autoCreateLocalDirectory(true), getRetryAdvice())
        .channel("gatewayDownloadsOutputChannel");
}