Network File System [网络文件系统]
NFS最初是由Sun Microsystems公司提出,全名Sun Network Filesystem,旨在提供一个独立于机器和操作系统的能够快速从错误(crash,failure)中恢复的分布式文件系统。任何系统都要从这个系统的定位来看,也就是这个系统要达到什么目标。提出NFS的论文Design and Implemenatation of the Sun Network Fielsystem阐明,主要目标有五个:
- Machine and Operating System Independence,也就是说此文件系统不能依赖特定的计算机和操作系统;
- Crash Recovery,文件系统要能从错误中快速回复,这里指的主要是server crash;
- Transparent Access,文件系统虽然是分布式的但是用起来应该和local file system是一样的;
- UNIX Semantics Matintained in Client,这个和上一个目标相关,因为要想local file system一样所以要使用和unix一样的语义[当时的操作系统是unix的天下];
- Reasonable Performance,这个好理解,不能说换了分布式,performance就差到没朋友,那谁还用?
但是!个人认为最重要的目标是要提供一个简单并且可以快速从server crash中恢复的操作系统!因为首先要考虑环境,在一个multiple client和single server的环境中如果server挂了,整个系统就挂了,只要server再剩下的操作都好说,所以最主要就是要recover from server crash。围绕这个主题,NFS提供了很多即便到今天依然被广泛使用和讨论甚至研究的理念。
Stateful vs Stateless
首先设想一下,一个client想要读取server的文件,它首先发送请求open,然后server会给client一个descriptor方便后续client的request,这样做的好处就是server可以提供非常快速的response,因为server已经知道client要操作什么文件,后面只要根据client的需求操作就好了。但是问题是,如果server挂了重启后,那个descriptor可能就没了,client再发送请求只能获得一个错误回复。更坏的情况是,client挂了,server还在等待client后续操作,但是client重启后好似失意,根本不知道。例如,server等待close操作,但是client对server发送一系列请求后挂了,结果再回复根本不可能给server发close,那么server就将永远打开文件。所以这种stateful procotol不适合NFS,相反stateless procotol更加轻量级,server不存储任何client的相关状态,所有请求都是一次的,这样做虽然performance不如stateful,但是可以提供更加简单高效的回复机制。
File Handle
句柄是用来支持NFS的stateless protocol的基础数据结构,它本质是一种定位信息,让client能够准确的找到存在server上的信息。它主要包含三个信息,volume identifier, inode number, generation number。volume identifier用来确定server上的文件系统类型[因为NFS是系统无关的,所以server上可能有着不同的文件系统],inode number用来确定具体的文件位置,generation number用来辨别拥有相同inode信息的不同文件。所以这三个信息可以唯一确定server上的一个文件。例如一个LOOKUP操作,client向server发送一个LOOKUP请求,之后client会获得整个文件目录的file handle,之后client再向server发送文件目录的file handle和其中一个文件名,然后server会返回给client对应的那个被请求文件的file handle。一旦获得特定文件的file hande就可以进行相应的读写操作。这里需要注意,在NFS中获得file handle的过程是一步一步的,比如一个文件/root/doc/foo.txt,client想获得foo.txt,需要经过三次LOOKUP并获得三个file handle。
Idempotency
幂等性在分布式系统的可靠性方面是一个非常重要的特性,已经有单独的blog介绍,这里就一笔掠过。
Caching
因为server上是不存任何状态信息的,所以cache说的主要是client-side cache。首先client会cache读取的数据,这个很好理解,cache之后就不用每次都要找server拿了。同时client还是cache写操作,这个好处在于可以合理组织优化一系列写操作,比如原本可能需要三个写操作的事,可以通过一个写操作完成。
Consistency
随着Caching而来的是consistency问题,怎么保证每个client和server的数据是一致的?NFS主要通过两个操作来完成,第一个叫flush-on-close或者是[flush-on-open,其实两个是一个意思]的语义操作,就是说当一个client完成了对一个文件的所有操作并且close了文件,client会flush所有更新到server。第二个是checking before using,就是每个client在使用一个文件之前会和server交互确定当前的file是最新的。基于这两个操作,可以基本保证一致性,这里要说以下,这里的一致性操作和高级的版本控制中的复杂一致性逻辑是不同的[后者建立与前者之上,并根据不同的情况实现一致性控制逻辑]。