1

我的 Java Web 应用程序使用 NFS 文件系统,我使用 FileOutputStream 打开、写入多个块然后关闭文件。

从探查器统计信息中,我发现 stream.write(byte[] payload,int begin, int length) 甚至 stream.flush() 都需要零毫秒。只有方法调用 stream.close() 需要非零毫秒。

似乎 java FileOutputStream 的 write() 或 flush() 并没有真正导致 NFS 客户端向 NFS 服务器发送数据。是否有任何其他 Java 类可以使 NFS 客户端实时刷新数据?还是需要进行一些 NFS 客户端调整?

4

2 回答 2

1

您可能遇到了 Unix 客户端缓存。O'Reilly NFS 书中有很多细节。

但简而言之:

当多台机器读取和写入同一个文件时,使用缓冲区缓存并允许异步线程集群多个缓冲区会带来一些问题。为了防止同一文件的多个读取器和写入器的文件不一致,NFS 制定了关闭时刷新策略:当文件关闭时,文件的所有部分填充的 NFS 数据缓冲区都将写入 NFS 服务器。

对于 NFS 版本 3 客户端,在稳定标志设置为关闭的情况下完成的任何写入都将通过提交操作强制到服务器的稳定存储上。

NFS 缓存一致性使用一种称为接近打开缓存一致性的方法——也就是说,您必须在服务器(和其他客户端)获得一致的最新文件视图之前关闭文件。您看到了这种方法的缺点,该方法旨在最大限度地减少服务器命中。

Java 很难避免缓存。open() O_DIRECT如果您使用的是 Linux,则需要设置文件标志;有关更多信息,请参阅此答案https://stackoverflow.com/a/16259319/5851520,但基本上它会禁用该文件的客户端操作系统缓存,尽管不是服务器的。

不幸的是,标准 JDK 没有公开O_DIRECT. 如这里所讨论的:强制 JVM 在没有页面缓存的情况下执行所有 IO(例如 O_DIRECT) - 本质上,您自己使用 JNI 或使用不错的 3rd 方库。我听说过关于 JNA 的好消息:https ://github.com/java-native-access/jna ...

或者,如果您可以控制客户端挂载点,则可以sync按照NFS 手册使用挂载选项。它说:

如果在挂载点上指定了同步选项,则任何将数据写入该挂载点上的文件的系统调用都会导致该数据在系统调用将控制权返回给用户空间之前刷新到服务器。这在客户端之间提供了更大的数据缓存一致性,但性能成本很高。

这可能是您正在寻找的。

于 2016-08-16T18:10:04.237 回答
-2

通常,Java 的流不保证flush可能除了刷新所涉及的 Java 类中的缓冲区之外的效果。

为了克服这个限制,可以使用 Java NIO 的 Channels,参见例如https://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html#force(boolean)。但是,如果“文件不驻留在本地设备上,则不会做出这样的保证”。而 Java 无法做出这样的保证,因为底层的远程文件系统或协议可能根本无法提供该功能。但是,您应该能够(几乎)实现与@SusanW 提到force()的本机访问相同级别的同步。O_DIRECT

于 2016-08-17T12:12:40.887 回答