REST endpoints in Spring Integration make messaging channels multithreaded

你说的曾经没有我的故事 提交于 2020-01-13 19:45:28

问题


I have a very simple Spring Boot application, which provides a couple of restful endpoints and this is supposed to drive a sftp file being uploaded to a sftp server. My requirement is that if there is more than one file, the files should be queued. I expected to achieve this with the default behaviour of the sftp spring integration workflow, as i read that DirectChannel automatically queues files. To test the behaviour I do the following:

  1. Send a big file, blocking the channel for a while by calling an endpoint.
  2. Send a smaller file by calling an endpoint.

Expected result: the smaller file is queued onto a channel and is processed after the uploading of the bigger file is finished. Actual result: A new connection to sftp server is opened and the smaller file is uploaded there without being queued, while the bigger file continues transmission.

There are two files in my application:

DemoApplication.java

@SpringBootApplication
@IntegrationComponentScan
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public SessionFactory<LsEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost("localhost");
        factory.setPort(22);
        factory.setUser("tester");
        factory.setPassword("password");
        factory.setAllowUnknownKeys(true);
        return factory;
    }

    @Bean
    @ServiceActivator(inputChannel = "toSftpChannel")
    public MessageHandler handler() {
        SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
        handler.setRemoteDirectoryExpression(new LiteralExpression("/"));
        return handler;
    }

    @MessagingGateway
    public interface MyGateway {

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

DemoController.java

@RestController
public class DemoController {

    @Autowired
    MyGateway gateway;

    @RequestMapping("/sendFile")
    public void sendFile() {
        File file = new File("C:/smallFile.txt");
        gateway.sendToSftp(file);
    }

    @RequestMapping("/sendBigFile")
    public void sendBigFile() {
        File file = new File("D:/bigFile.zip");
        gateway.sendToSftp(file);
    }
}

I'm a complete newbie to spring and i'm not sure entirely that my sftp channels get created here properly, my guess would be that a new one gets created every time i do a sendToSftp call. Any help on how to achieve the queue behaviour in this case would be appreciated.


回答1:


You don't have a queue here because each HTTP request is performed in its own thread. Right, you maybe queued there anyway when the pool of http threads is exhausted, but that doesn't look in your simple use-case with just two requests.

You can achieve a queue behavior there anyway, but you should declare your toSftpChannel as a QueueChannel bean.

That way downstream process will always be performed on the same thread and the next message is pulled from the queue exactly after the first one.

See Reference Manual for more information.

UPDATE

Since you use FtpMessageHandler which is one-way component, but you still need some reply to the MVC controller's methods, only the way to do that is to have a @Gateway method with non-void return and of course we need to send the reply somehow.

For this purpose I suggest to use PublishSubscribeChannel:

@Bean
@BridgeTo
public MessageChannel toSftpChannel() {
    return new PublishSubscribeChannel();
}

@Bean
@ServiceActivator(inputChannel = "toSftpChannel")
@Order(0)
public MessageHandler handler() {
    SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
    handler.setRemoteDirectoryExpression(new LiteralExpression("/"));
    return handler;
}

This way we have two subscribers to the toSftpChannel. With the @Order(0) we ensure that @ServiceActivator is the first subscriber since we need to perform SFTP transfer first. With the @BridgeTo we add a second BridgeHandler to the same PublishSubscribeChannel. Its purpose is just to get a replyChannel header and send the request message there. Since we don't use any threading the BridgeHandler will be performed exactly after finish of the transfer to the SFTP.

Of course instead of BridgeHandler you can have any other @ServiceActivator or @Transfromer to return as a reply not a request File, but anything else. For example:

@ServiceActivator(inputChannel = "toSftpChannel")
@Order(1)
public String transferComplete(File payload) {
    return "The SFTP transfer complete for file: " + payload;
}


来源:https://stackoverflow.com/questions/45362706/rest-endpoints-in-spring-integration-make-messaging-channels-multithreaded

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!