Spring integration 同时为每个Ftp文件运行Spring集成流
我有一个使用Java DSL配置的集成流,它使用Spring integration 同时为每个Ftp文件运行Spring集成流,spring-integration,spring-integration-dsl,spring-integration-sftp,Spring Integration,Spring Integration Dsl,Spring Integration Sftp,我有一个使用Java DSL配置的集成流,它使用Ftp.inboundChannelAdapter从Ftp服务器提取文件,然后将其转换为JobRequest,然后我有一个.handle()方法触发批处理作业,所有操作都按照要求进行,但FTP文件夹中的每个文件的进程都在按顺序运行 我在Transformer端点中添加了currentThreadName,它为每个文件打印相同的线程名称 这是我到现在为止一直在尝试的 1.任务执行器bean @Bean public TaskExecutor
Ftp.inboundChannelAdapter
从Ftp服务器提取文件,然后将其转换为JobRequest
,然后我有一个.handle()
方法触发批处理作业,所有操作都按照要求进行,但FTP文件夹中的每个文件的进程都在按顺序运行
我在Transformer端点中添加了currentThreadName
,它为每个文件打印相同的线程名称
这是我到现在为止一直在尝试的
1.任务执行器bean
@Bean
public TaskExecutor taskExecutor(){
return new SimpleAsyncTaskExecutor("Integration");
}
2.整合流程
@Bean
public IntegrationFlow integrationFlow(JobLaunchingGateway jobLaunchingGateway) throws IOException {
return IntegrationFlows.from(Ftp.inboundAdapter(myFtpSessionFactory)
.remoteDirectory("/bar")
.localDirectory(localDir.getFile())
,c -> c.poller(Pollers.fixedRate(1000).taskExecutor(taskExecutor()).maxMessagesPerPoll(20)))
.transform(fileMessageToJobRequest(importUserJob(step1())))
.handle(jobLaunchingGateway)
.log(LoggingHandler.Level.WARN, "headers.id + ': ' + payload")
.route(JobExecution.class,j->j.getStatus().isUnsuccessful()?"jobFailedChannel":"jobSuccessfulChannel")
.get();
}
3.我还读入了另一个SO线程,我需要ExecutorChannel
,所以我配置了一个,但我不知道如何将此通道注入我的Ftp。inboundAdapter
,从日志中可以看到该通道始终是integrationFlow.channel#0
,我猜它是DirectChannel
@Bean
public MessageChannel inputChannel() {
return new ExecutorChannel(taskExecutor());
}
我不知道我在这里遗漏了什么,或者我可能没有正确理解Spring消息传递系统,因为我对Spring和Spring集成非常陌生
谢谢你的帮助
感谢您可以简单地将
ExecutorChannel
注入到流中,框架将把它应用到SourcePollingChannelAdapter
。因此,将inputChannel
定义为bean,您只需执行以下操作:
.channel(inputChannel())
在您的.transform(fileMessageToJobRequest(importUserJob(step1()))之前)
。
请参阅文档中的更多内容:
另一方面,要根据.taskExecutor(taskExecutor())
配置并行处理文件,只需将.maxMessagesPerPoll(20)
设置为1
。AbstractPollingEndpoint
中的逻辑如下:
this.taskExecutor.execute(() -> {
int count = 0;
while (this.initialized && (this.maxMessagesPerPoll <= 0 || count < this.maxMessagesPerPoll)) {
if (pollForMessage() == null) {
break;
}
count++;
}
this.taskExecutor.execute(()->{
整数计数=0;
虽然(this.initialized&&(this.maxMessagesPerPollExecutorChannel
您可以简单地注入到流中,它将被框架应用到SourcePollingChannelAdapter
。因此,将inputChannel
定义为bean,您只需执行以下操作:
.channel(inputChannel())
在您的.transform(fileMessageToJobRequest(importUserJob(step1()))之前)
。
请参阅文档中的更多内容:
另一方面,要根据.taskExecutor(taskExecutor())
配置并行处理文件,只需将.maxMessagesPerPoll(20)
设置为1
。AbstractPollingEndpoint
中的逻辑如下:
this.taskExecutor.execute(() -> {
int count = 0;
while (this.initialized && (this.maxMessagesPerPoll <= 0 || count < this.maxMessagesPerPoll)) {
if (pollForMessage() == null) {
break;
}
count++;
}
this.taskExecutor.execute(()->{
整数计数=0;
while(this.initialized&&(this.maxMessagesPerPoll),因此pollerspec中的taskExecutor()
方法允许poller轮询消息并将其交给另一个线程,然后将轮询的线程释放以轮询下一组消息…是这样吗?是的,这是正确的。至少有两个线程:一个用于根据触发器配置安排定期任务。另一个(或多个)用于执行轮询任务。因此,是的,当在不同的线程上执行此类轮询任务时,控制返回调度程序以启动新的周期性任务。从技术上讲,下游ExecutorChannel
将给我们带来类似的副作用-只要我们将作业转移到不同的线程,调度程序就可以执行此任务e下一个轮询周期。在我将maxMessagesPerPoll
更改为1后,每个文件的批处理作业现在在不同的线程上运行,即使我没有ExecutorChannel
,因此当轮询器一次读取两条或多条消息并将其交给ExecutorChannel
时,ExecutorChannel
非常有用e通道在不同的线程上同时处理每条消息。ExecutorChannel
在我将maxMessagesPerPoll
设置为1时不是必需的。正确吗?只需再次澄清我的疑问我知道你在回答中解释了类似的问题。你的观察是正确的:确实没有必要增加线程的开销如果这对您的逻辑来说足够的话,则进行移位。因此pollerspec中的taskExecutor()
方法允许poller轮询消息并将其交给另一个线程,然后将轮询的线程释放以轮询下一组消息…是这样吗?是的,这是正确的。至少有两个线程:一个用于根据触发器配置安排定期任务。另一个(或多个)用于执行轮询任务。因此,是的,当在不同的线程上执行此类轮询任务时,控制返回调度程序以启动新的周期性任务。从技术上讲,下游ExecutorChannel
将给我们带来类似的副作用-只要我们将作业转移到不同的线程,调度程序就可以执行此任务e下一个轮询周期。在我将maxMessagesPerPoll
更改为1后,每个文件的批处理作业现在在不同的线程上运行,即使我没有ExecutorChannel
,因此当轮询器一次读取两条或多条消息并将其交给ExecutorChannel
时,ExecutorChannel
非常有用e通道在不同的线程上同时处理每条消息。ExecutorChannel
在我将maxMessagesPerPoll
设置为1时不是必需的。正确吗?只需再次澄清我的疑问我知道你在回答中解释了类似的问题。你的观察是正确的:确实没有必要增加线程的开销如果这对你的逻辑足够的话。