问题
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:
- Send a big file, blocking the channel for a while by calling an endpoint.
- 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