为什么java FileOutputStream 的write() 或flush() 不能使NFS 客户端真正向NFS 服务器发送数据?

Why java FileOutputStream's write() or flush() doesn't make NFS client really send data to NFS server?

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

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

似乎java FileOutputStream 的write() 或flush() 并没有真正导致NFS 客户端向NFS 服务器发送数据。有没有其他的Java class会让NFS客户端实时刷新数据?或者需要进行一些 NFS 客户端调整?

您可能 运行 喜欢 Unix 客户端缓存。有很多细节here in the O'Reilly NFS book

但简而言之:

Using the buffer cache and allowing async threads to cluster multiple buffers introduces some problems when several machines are reading from and writing to the same file. To prevent file inconsistency with multiple readers and writers of the same file, NFS institutes a flush-on-close policy: All partially filled NFS data buffers for a file are written to the NFS server when the file is closed.

For NFS Version 3 clients, any writes that were done with the stable flag set to off are forced onto the server's stable storage via the commit operation.

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

从 Java 开始很难避免缓存。如果您使用 Linux,则需要设置文件 open() O_DIRECT 标志;有关更多信息,请参阅此答案 ,但基本上它会禁用该文件的客户端 OS 缓存,但不会禁用服务器的缓存。

不幸的是,标准 JDK 不公开 O_DIRECT。如此处所讨论:Force JVM to do all IO without page cache (e.g. O_DIRECT) - essentially, use JNI youself or use a nice 3rd party lib. I've heard good things about JNA: https://github.com/java-native-access/jna ...

或者,如果您可以控制客户端挂载点,则可以使用 sync 挂载选项,如 NFS manual 所示。它说:

If the sync option is specified on a mount point, any system call that writes data to files on that mount point causes that data to be flushed to the server before the system call returns control to user space. This provides greater data cache coherence among clients, but at a significant performance cost.

这可能就是您要找的。

一般来说,Java 的流不保证 flush 的效果,除了可能刷新 Java 类 中的缓冲区。

为了克服这个限制,Java 可以使用 NIO 的通道,例如 https://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html#force(boolean)。但是,如果 "the file does not reside on a local device then no such guarantee is made." 和 Java 不能做出这样的保证,因为底层的远程文件系统或协议可能根本无法提供那个功能。但是,您应该能够(几乎)实现与 force() 相同级别的同步,您可以从 @SusanW 提到的本机 O_DIRECT 访问中获得。