首页 > 解决方案 > Spring 集成:使用 NioFileLocker 的入站通道导致只读文件系统异常

问题描述

版本:

Spring: 5.2.16.RELEASE
Spring Integrations: 5.3.9.RELEASE
Ubuntu: focal [20.04.3 LTS]
Java (build and runtime): 11

请参阅下面粘贴的堆栈跟踪。我的入站通道定义如下:

<int-file:inbound-channel-adapter id="channelIn" directory="${channel.dir}" auto-create-directory="false" use-watch-service="true" filter="channelFilter" watch-events="CREATE,MODIFY">
  <int-file:locker ref="channelLocker"/>
  <int:poller fixed-delay="${channel.polling.delay}" max-messages-per-poll="${channel.polling.maxmsgs}"></int:poller>
</int-file:inbound-channel-adapter>

<bean id="channelLocker" class="org.springframework.integration.file.locking.NioFileLocker"/>

我的ServiceActivator逻辑是这样的:

  @Autowired
  @Qualifier("channelLocker")
  private NioFileLocker nioFileLocker;

  @ServiceActivator(inputChannel = "channelIn")
  public void processResultsFile(File results)
  {
    if (!results.exists() || !results.isFile()) {
      logger.error("could not process invalid file {}", results.getAbsolutePath());
      return;
    }

    try (InputStream in = new BufferedInputStream(new FileInputStream(results))) {
      // Process input stream ...
    } catch (Exception ex) {
      logger.error("could not process file {}", results.getAbsolutePath(), ex);
    }

    nioFileLocker.unlock(results);
  }

一篇文章讨论了使用 nio FileLock 的挑战。但我不相信我对 nio FileLock 的使用是错误的。相反,我怀疑我正在读取的已安装的网络文件系统是只读的,它会给 nio FileLock 带来问题。所述文件系统的挂载点如下所示:

/etc/auto.pathto on /pathto type autofs (rw,relatime,fd=6,pgrp=634173,timeout=300,minproto=5,maxproto=5,indirect,pipe_ino=2386182)

现在,我将不得不删除 nio FileLock,因为它会妨碍而不是帮助。但是,在从网络只读文件系统读取资源时,是否有某种方法仍然可以获得文件锁定?

22:01:53.262 [task-scheduler-2] ERROR org.springframework.integration.handler.LoggingHandler - org.springframework.messaging.MessagingException: Failed to lock file: /pathto/myfile.txt; nested exception is java.io.FileNotFoundException: /pathto/myfile.txt (Read-only file system)
    at org.springframework.integration.file.locking.NioFileLocker.lock(NioFileLocker.java:54)
    at org.springframework.integration.file.DefaultDirectoryScanner.tryClaim(DefaultDirectoryScanner.java:85)
    at org.springframework.integration.file.FileReadingMessageSource.doReceive(FileReadingMessageSource.java:362)
    at org.springframework.integration.file.FileReadingMessageSource.doReceive(FileReadingMessageSource.java:93)
    at org.springframework.integration.endpoint.AbstractMessageSource.receive(AbstractMessageSource.java:184)
    at org.springframework.integration.endpoint.SourcePollingChannelAdapter.receiveMessage(SourcePollingChannelAdapter.java:212)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:408)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.pollForMessage(AbstractPollingEndpoint.java:377)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$null$3(AbstractPollingEndpoint.java:324)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor.lambda$execute$0(ErrorHandlingTaskExecutor.java:57)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
    at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:55)
    at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$createPoller$4(AbstractPollingEndpoint.java:321)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.io.FileNotFoundException: /pathto/myfile.txt (Read-only file system)
    at java.base/java.io.RandomAccessFile.open0(Native Method)
    at java.base/java.io.RandomAccessFile.open(RandomAccessFile.java:345)
    at java.base/java.io.RandomAccessFile.<init>(RandomAccessFile.java:259)
    at java.base/java.io.RandomAccessFile.<init>(RandomAccessFile.java:214)
    at org.springframework.integration.file.locking.FileChannelCache.tryLockFor(FileChannelCache.java:61)
    at org.springframework.integration.file.locking.NioFileLocker.lock(NioFileLocker.java:51)
    ... 20 more

标签: springspring-integration

解决方案


您对只读文件系统的假设可能是正确的。至少我看到我们有类似的逻辑FileChannelCache

FileChannel newChannel = new RandomAccessFile(fileToLock, "rw").getChannel();

所以,我们确实在这里失败了。我相信即使我们只将它设置为r,我们也会在逻辑上进一步失败,例如:

public final FileLock tryLock() throws IOException {
    return tryLock(0L, Long.MAX_VALUE, false);
}
...
if (shared && !readable)
        throw new NonReadableChannelException();
if (!shared && !writable)
        throw new NonWritableChannelException();

因此 aNioFileLocker不适合您。

您可以实现自己的FileLocker,但在大多数情况下,我们只建议使用共享持久性MetadataStoreFileSystemPersistentAcceptOnceFileListFilter用于该文件入站通道适配器。这样,您的应用程序的所有实例都将访问同一个存储,并且只有一个实例会处理该文件。所有其他实例将跳过它并转到另一个实例。

在文档中查看更多信息:https ://docs.spring.io/spring-integration/docs/current/reference/html/file.html#file-reading


推荐阅读