2.1 GFS的外部接口和架构
让我们从GFS的接口设计和架构设计说起吧。
2.1.1 GFS的外部接口
GFS采用了人们非常熟悉的接口,但是并没有实现POSIX的标准文件接口。GFS通常的操作包括create,delete,open,close,read,write,record append等,这些接口非常类似于POSIX定义的标准文件接口,但是不完全一致。
create,delete,open,close这几个接口的语义和POSIX标准接口类似,这里就不逐一强调说明了。下面详细介绍write和record append这两个接口的语义。
● write(随机写):可以将任意长度的数据写入指定文件的位置,这个文件位置也被称为偏移(offset)。
● record append(尾部追加写):可以原子地将长度小于16MB的数据写入指定文件的末尾。GFS之所以设计这个接口,是因为record append不是简单地将offset取值设置为文件末尾的write操作,而是不同于write的一个操作,并且是具有原子性的操作(后面的2.3节会解释原子性)。
write和record append都允许多个客户端并发操作一个文件,也就是允许一个文件被多个客户端同时打开和写入。
2.1.2 GFS的架构
GFS的架构如图2.1所示。
图2.1 GFS的架构(此图参考GFS的论文[1])
GFS的主要架构组件有GFS client、GFS master和GFS chunkserver。一个GFS集群包括一个master和多个chunkserver,集群可以被多个GFS客户端访问。三个组件的详细说明如下:
● GFS客户端(GFS client)是运行在应用(application)进程里的代码,通常以SDK形式存在。
● GFS中的文件被分割成固定大小的块(chunk),每个chunk的长度固定为64MB。GFS chunkserver把这些chunk存储在本地的Linux文件系统中,也就是本地磁盘中。通常每个chunk会被保存三个副本(replica),也就是会被保存到三个chunkserver里。一个chunkserver会保存多个不同的chunk,每个chunk都会有一个标识,叫作块柄(chunk handle)。
● GFS master维护文件系统的元数据(metadata),包括:
■ 名字空间(namespace,也就是常规文件系统中的文件树)。
■ 访问控制信息。
■ 每个文件由哪些chunk构成。
■ 每个chunk的副本都存储在哪些chunkserver上,也就是块位置(chunk location)。
在这样的架构下,几个组件之间有如下交互过程。
1.客户端与master的交互
客户端可以根据chunk大小(即固定的64MB)和要操作的offset,计算出操作发生在第几个chunk上,也就是chunk的块索引号(chunk index)。在文件操作的过程中,客户端向master发送要操作的文件名和chunk index,并从master中获取要操作的chunk的chunk handle和chunk location。
客户端获取到chunk handle和chunk location后,会向chunk location中记录的chunkserver发送请求,请求操作这个chunkserver上标识为chunk handle的chunk。
如果一次读取的数据量超过了一个chunk的边界,那么客户端可以从master获取到多个chunk handle和chunk location,并且把这次文件读取操作分解成多个chunk读取操作。
同样,如果一次写入的数据量超过了一个chunk的边界,那么这次文件写入操作也会被分解为多个chunk写入操作。当写满一个chunk后,客户端需要向master发送创建新chunk的指令。
2.客户端向chunkserver写数据
客户端向要写入的chunk所在的三个chunkserver发送数据,每个chunkserver收到数据后,都会将数据写入本地的文件系统中。客户端收到三个chunkserver写入成功的回复后,会发送请求给master,告知master这个chunk写入成功,同时告知application写入成功。
这个写流程是高度简化和抽象的,实际的写流程更复杂,要考虑写入类型(是随机写还是尾部追加写),还要考虑并发写入(后面的2.2节会详细描述写流程,解释GFS是如何处理不同的写入类型和并发写入的)。
3.客户端从chunkserver读数据
客户端向要读取的chunk所在的其中一个chunkserver发送请求,请求中包含chunk handle和要读取的字节范围(byte range)。chunkserver根据chunk handle和byte range,从本地的文件系统中读取数据返回给客户端。与前面讲的写流程相比,这个读流程未做太多的简化和抽象,但对实际的读流程还会做一些优化(相关优化和本书主题关系不大,就不展开介绍了)。