FileSystemPersistentAcceptOnceFileListFilter无法识别文件求助
问题描述
环境:OpenJDK Java 17、Spring Boot 2.7.4 + Spring Integration
使用如下代码监听目录文件:
@Bean public MessageChannel fileChannel() { return new DirectChannel(); } @Bean @InboundChannelAdapter(value = "fileChannel", poller = @Poller(fixedDelay = "30000")) public MessageSource<File> watchSourceDirectory() { FileReadingMessageSource source = new FileReadingMessageSource(); source.setDirectory(new File(appConfig.getLocal().getSourceDir())); source.setAutoCreateDirectory(true); CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<>(); compositeFileListFilter.addFilter(new RegexPatternFileListFilter(appConfig.getLocal().getFilePattern())); compositeFileListFilter.addFilter(new LastModifiedFileListFilter(10)); compositeFileListFilter.addFilter(new FileSystemPersistentAcceptOnceFileListFilter(metadataStore, "")); source.setFilter(compositeFileListFilter); return source; } // end watchSourceDirectory()
现象:
- 元数据存储表已更新文件及其时间戳,但文件从未进入
fileChannel处理 - 此前应用运行数月无异常,执行
touch命令修改文件时间戳后才触发处理
怀疑是时序问题,寻求解决建议。
分析与解决建议
1. 修正LastModifiedFileListFilter的时序过滤逻辑
LastModifiedFileListFilter(10)的参数是文件修改时间与当前时间的最小间隔(单位:秒),仅当文件完成写入超过10秒后才会被纳入处理列表。如果文件生成时间与轮询时间点的间隔小于10秒,第一次轮询会被过滤,同时FileSystemPersistentAcceptOnceFileListFilter会将该文件标记为已处理,后续轮询就不会再触发。
解决方式:
- 根据实际文件写入耗时调整参数值,比如改为
0(需确保文件写入是原子操作); - 若无法缩短间隔,可调整过滤器执行顺序?不,正确顺序是先过滤未完成写入的文件,再做持久化去重,所以更合理的做法是结合下文的元数据过滤器优化。
2. 优化FileSystemPersistentAcceptOnceFileListFilter的时间戳校验逻辑
你使用的构造器FileSystemPersistentAcceptOnceFileListFilter(metadataStore, ""),默认会将文件路径+修改时间戳作为元数据的键。如果文件写入时时间戳未稳定(部分文件系统存在时间戳延迟更新),第一次轮询时LastModifiedFilter过滤掉文件,但元数据已存入当时的时间戳;后续即使文件时间戳稳定,元数据记录的旧时间戳会被判定为已处理,直到touch更新时间戳才会重新触发。
解决方式:
- 使用支持文件系统同步的构造器,开启实时时间戳校验:
new FileSystemPersistentAcceptOnceFileListFilter(metadataStore, "", true)
第三个参数forFsSync设为true后,过滤器会对比元数据中的时间戳与文件当前的实际修改时间,若文件时间戳更新则重新处理。
3. 调整轮询策略或改用监听模式
定时轮询的固定间隔可能和文件写入时间点产生时序冲突:比如轮询在0/30/60秒执行,文件在25秒写入完成,距离下一次轮询仅5秒,小于10秒阈值被过滤,同时元数据标记为已处理;后续轮询时文件时间戳虽满足间隔,但元数据已存在记录,无法触发处理。
解决方式:
- 缩短轮询间隔(如
fixedDelay = "15000"),减少时间窗口冲突概率; - 启用
FileReadingMessageSource的文件系统监听模式,基于事件触发而非定时轮询,更及时捕获文件变化:
source.setUseWatchService(true); source.setWatchEvents(FileReadingMessageSource.WatchEventType.CREATE, FileReadingMessageSource.WatchEventType.MODIFY);
4. 检查元数据存储的一致性
确认元数据存储(数据库/Redis等)中的记录是否存在路径编码、分隔符不一致的问题,比如不同系统下的路径格式差异,导致过滤器无法正确匹配已处理文件。
解决方式:
- 直接查看元数据存储中的记录,对比文件实际路径与存储路径是否完全一致;
- 若使用数据库存储,确认表字符集支持文件路径中的特殊字符。
内容的提问来源于stack exchange,提问作者user925406

