有2个服务通过NFS去共享文件,一个服务创建,一个服务读取。
问题: 偶先的B服务查找不到A创建的文件
排查过程如下
1.查看A和B服务POD的磁盘挂载和使用情况
#查看磁盘挂载和使用情况
df -h
发现使用了nfs
挂载以及挂载的目录
2.复现文件,去日照查找指定文件
我会在B服务查找文件的代码,前后打印当前文件夹下的文件
listfiles("list - pre") // 增加日志
xxxfile.exits()
listfiles("list - post")// 增加日志
#查找日志
grep 'list - ' *.log
07:28:40,382 ...
07:28:45,390 ...
07:28:50,400 ...
07:28:55,411 ...
07:29:00,414 ...
发现在当时的场景中文件夹中的确没有要查询的文件。
3.查找指定问题文件
#查找文件
find /nfs/pic -name abc.jpg
find . -name abc.jpg
#查找目录
find . -type d -name abc
发现文件时存在的
4.查找文件详细信息创建时间
ll --full-time
nobody nobody 2023-10-11 07:08:40.144 abc.jpg
发现创建时间是在程序读取后的时间。
就怀疑是NFScache问题,查找相关资料总结如下
1.在传统磁盘中,所有的数据都会被缓存在PageCache中,修改过的Page会被异步刷回服务端,所以磁盘的延时都比较低。
2.但在NFS文件系统中,NFS不会将新创建的文件或者新写入的内容缓存在PageCache中,而是尽快刷回NAS服务端。
因此,当多个服务器共享一个NFS协议文件系统时,NAS所有的操作都会比磁盘多一次网络调用开销,这个开销一般在100us ~ 1ms之间。其中,尽快刷回NAS服务端,则涉及NAS所提供的如下多节点一致性模型:
NFS会缓存目录或者文件的属性(FileAttr),操作系统会根据FileAttr是否发生变化,来判断这个目录或者文件是否在其他ECS被修改过。同时,加载FileAttr后,操作系统在T时间内,会认为Cache(例如,文件的内容、目录下的文件列表)有效;超过T时间后,会去服务端重新获取一次FileAttr,如果FileAttr未发生变化,操作系统会认为与此文件相关的Cache全部还是有效的。
说明
- T为自适应的值。默认值:1s~60s。
- 文件内容Cache:指缓存了这个文件的内容。
- 目录子项Cache:缓存了这个目录下存在哪些文件、不存在哪些文件。
文件内容Cache示例:
我们遇到的问题就是这个原因,第一次查询文件时,发现文件不存在就缓存了一个不存在的cache,导致下次直接查缓存,还是不存在。
目录子项Cache示例:
更多有关NFS超时的最终一致性模型内容,请参见NFS。
由于超时的最终一致性无法保证ECS-2可以立刻读ECS-1写入的数据。因此,为了提升一致性,NFS还提供了基于文件的CTO(close-to-open)一致性保证,即当两个及以上计算节点同时读写相同的文件时,ECS-1的修改在ECS-2不一定能立即看到。但是,一旦ECS-1打开一个文件,写入若干数据,然后执行了文件关闭操作,之后在任何一个计算节点重新打开该文件都可以保证能访问到新写入的数据。
例如,生产者ECS生产了文件X,生产完毕后执行了close。
然后给消息队列发一条消息说,文件X生产完毕。
消费者ECS订阅消息队列,读到消息X(文件X生产完毕),
此时,消费者ECS再去open这个文件,通过open返回的fd去读取这个文件,则一定能够读到文件X的所有内容。如果消费者ECS在生产者ECS生产完毕之前,就open了文件X,并且持有了fd,当收到消息后,直接用这个fd去读,是不保证能够读取到最新数据的。
问题简化为:ECS-1创建了文件abc,但是ECS-2需要过一段时间才能看到ECS-1创建的文件abc,有时会延迟1s,有时甚至会到1分钟,这是为什么?
综合一下上面的一坨:这是Lookup Cache导致的,符合预期T时间。例如,ECS-2在ECS-1创建文件abc前进行了访问,导致ECS-2发生文件不存在,于是缓存了一条文件abc不存在的记录。在T时间内,由于FileAttr还没有过期,ECS-2再次访问时,仍会访问第一次缓存到文件abc不存在的记录。
方案一
在程序中 循环检测(比如检测10次),注意间隔的时长 不要太长1-2s
,我们这边设置5秒时 有问题。
方案二
关闭ECS-2的Nagtive Lookup Cache,不缓存不存在的文件。该方案开销最小。
挂载时,添加lookupcache=positive(默认值lookupcache=all)字段,挂载命令如下所示:
sudo mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,lookupcache=positive file-system-id.region.nas.aliyuncs.com:/ /mnt
方案三
关闭ECS-2的所有缓存。该方案会导致性能非常差,请根据业务实际情况选择合适的方案。
挂载时,添加actimeo=0字段,挂载命令如下所示:
sudo mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,actimeo=0 file-system-id.region.nas.aliyuncs.com:/ /mnt
尽管上述解决方案可以在一定程度上减轻NFS文件同步延迟问题,但它们仍然需要处理许多复杂的技术细节。然而,有一种更简单和可行的方法,可以彻底摆脱NFS的问题,那就是使用云对象存储服务(如OSS,对象存储服务)。