我正在从Spring Integration中读取FileReadingMessageSource中的根目录,以检索正在进行的文件创建。场景是在根目录持续的基础上可能存在多个子目录。来自SI 4.3.1的WatchServiceDirectoryScanner用于获取在任何新子目录中创建的任何文件。
@Bean
public MessageSource<File> fileReadingMessageSource() {
CompositeFileListFilter<File> filters = new CompositeFileListFilter<>();
filters.addFilter(new SimplePatternFileListFilter("pattern*"));
//filters.addFilter(new LastModifiedFileListFilter());
FileReadingMessageSource fileSource = new FileReadingMessageSource();
String filePath = "root-directory";
fileSource.setDirectory(new File(filePath));
fileSource.setFilter(filters);
fileSource.setUseWatchService(true);
fileSource.setWatchEvents(FileReadingMessageSource.WatchEventType.CREATE,FileReadingMessageSource.WatchEventType.MODIFY,FileReadingMessageSource.WatchEventType.DELETE);
return fileSource;
}
@Bean
public IntegrationFlow readDirectoryFlow() {
return IntegrationFlows.from(
fileReadingMessageSource(),
e -> e.poller(Pollers.cron("*/5 * * * * *")))
.channel(fileInputChannel())
.handle(tailerRestart)
.handle(System.out::println)
.get();
}
在第一次轮询时,所有匹配模式的文件都可以通过消息资源获得,但如果以后在任何新的子目录中创建了任何新文件,则消息资源无法选择新的模式匹配文件。
我在日志中看到了以下DEBUG消息
DEBUG SourcePollingChannelAdapter - 在投票期间未收到任何消息,返回'false'
可能缺少什么?
答案 0 :(得分:2)
我刚刚编写了一些与您的代码非常接近的测试用例:
@Bean
public MessageSource<File> fileReadingMessageSource() {
CompositeFileListFilter<File> filters = new CompositeFileListFilter<>();
filters.addFilter(new SimplePatternFileListFilter("*.watch"));
FileReadingMessageSource fileSource = new FileReadingMessageSource();
fileSource.setDirectory(tmpDir.getRoot());
fileSource.setFilter(filters);
fileSource.setUseWatchService(true);
fileSource.setWatchEvents(FileReadingMessageSource.WatchEventType.CREATE,
FileReadingMessageSource.WatchEventType.MODIFY,
FileReadingMessageSource.WatchEventType.DELETE);
return fileSource;
}
@Bean
public IntegrationFlow readDirectoryFlow() {
return IntegrationFlows
.from(fileReadingMessageSource(),
e -> e.poller(p -> p.cron("*/1 * * * * *")))
.handle(System.out::println)
.get();
}
测试代码如下:
@ClassRule
public static final TemporaryFolder tmpDir = new TemporaryFolder();
@Test
public void testWatchServiceMessageSource() throws Exception {
File newFolder1 = tmpDir.newFolder();
FileOutputStream file = new FileOutputStream(new File(newFolder1, "foo.watch"));
file.write(("foo").getBytes());
file.flush();
file.close();
File newFolder2 = tmpDir.newFolder();
file = new FileOutputStream(new File(newFolder2, "bar.watch"));
file.write(("bar").getBytes());
file.flush();
file.close();
file = new FileOutputStream(new File(tmpDir.getRoot(), "root.watch"));
file.write(("root").getBytes());
file.flush();
file.close();
Thread.sleep(10000);
}
我有这个日志:
GenericMessage [payload=C:\Users\abilan\AppData\Local\Temp\junit7602962373770028652\junit7776799219532481336\foo.watch, headers={id=50d44197-e0af-708a-6b61-2a2cfeec68da, timestamp=1473686655061}]
GenericMessage [payload=C:\Users\abilan\AppData\Local\Temp\junit7602962373770028652\junit813088196038861528\bar.watch, headers={id=8d80c853-19b6-f667-7950-d6de49d509ab, timestamp=1473686656062}]
GenericMessage [payload=C:\Users\abilan\AppData\Local\Temp\junit7602962373770028652\root.watch, headers={id=e585203b-41dc-cadb-6a36-4c9009a34701, timestamp=1473686657063}]
每一秒。
不确定你的问题在哪里......
您不需要.channel(fileInputChannel())
。它将在ednpoints之间自动创建。
配置:
.handle(tailerRestart)
.handle(System.out::println)
你应该确保tailerRestart
返回一些东西。虽然,根据我们的其他讨论,它没有:
@ServiceActivator
public void restartTailer(File input) throws Exception {
tailFileProducer.stop();
tailFileProducer.setFile(input);
tailFileProducer.start();
}
<强>更新强>
经过一些私人调查后,我们发现问题出在Spring Cloud Stream基础架构多次调用的FileReadingMessageSource.start()
上,导致重新实例化内部WatchService
对象。
FileReadingMessageSource.start()
必须修正为幂等:https://jira.spring.io/browse/INT-4108
Spring Cloud Stream已在版本1.1
中修复:https://github.com/spring-cloud/spring-cloud-stream/issues/525。
解决方法就像确保只调用FileReadingMessageSource.start()
一次:
FileReadingMessageSource fileSource = new FileReadingMessageSource() {
private final AtomicBoolean running = new AtomicBoolean();
@Override
public void start() {
if (!this.running.getAndSet(true)) {
super.start();
}
}
@Override
public void stop() {
if (this.running.getAndSet(false)) {
super.stop();
}
}
};