How to prevent duplicate Spring Integration servic

2019-03-01 02:24发布

I have a Spring Integration directory poller:

<task:executor id="filePollingExecutor" pool-size="1" />
<int:channel id="inboundFilesChannel" datatype="java.io.File" />
<int-file:inbound-channel-adapter id="inboundFilesAdapter" 
      channel="inboundFilesChannel"
      directory="/my/files/queue"
      prevent-duplicates="true">
  <int:poller id="poller" fixed-delay="1000" 
              max-messages-per-poll="1" 
              task-executor="filePollingExecutor" />
</int-file:inbound-channel-adapter>

In response to files appearing in the directory, I have a service activator which invokes a method on a service:

Unfortunately I'm finding that the service is consistently being called twice when a file arrives. Originally I thought this was due to having multiple executor threads, but you may notice above, I attempted to resolve that by tying the poller to a taskExecutor with a pool size of 1.

What I have been finding is that I can workaround the issue by increasing the delay between polls. I think the key is that it's longer than the time it takes to process a file.

<int:poller id="poller" fixed-delay="10000" 
            max-messages-per-poll="100" 
            task-executor="filePollingExecutor" />

However, that feels like a kludge rather than a fix.

Am I missing some configuration that I should be using to prevent duplicates?

It's possibly worth noting that I did try using a nio-locker, but the issue there is that part of the processing involves sending an email with the file attached to it. File locks prevented that from being done, as the file ceased to be readable for the duration of the lock.

1条回答
Fickle 薄情
2楼-- · 2019-03-01 03:27

This answer is based on the tip from Gary Russell in the comments above.

The reason for double-processing of files is that the root and web configs were both initialising file system listeners, and therefore processing each file twice.

My approach to avoiding having the file listeners in multiple contexts was as follows.

First define a web config which only picks up classes under the "web" package.

@Configuration
@ComponentScan(basePackages = { "com.myapp.web" })
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Create separate root configs which only load beans which are not in the "web" package. i.e.

@Configuration
@ComponentScan(basePackages = { "com.myapp.services" })
public class ServicesConfig {
}

An additional factor in the configuration that took a little while to work out, was that my servlet filters and web security config needed to be in the 'root' context rather than the web context.

查看更多
登录 后发表回答