2.2 GFS的写流程细节
本节我们详细讲解在前面的写数据过程中未提及的几个细节。
2.2.1 名字空间管理和锁保护
在写流程中,当要创建新文件和将数据写入新chunk时,客户端都需要联系master来操作master上的名字空间。
● 创建新文件:在名字空间创建一个新对象,该对象代表这个文件。
● 将数据写入新chunk中:向master的元数据中创建新chunk相关信息。
如果有多个客户端同时进行写入操作,那么这些客户端也会同时向master发送创建文件或创建新chunk的指令。master在同一时间收到多个请求,它会通过加锁的方式,防止多个客户端同时修改同一个文件的元数据。
2.2.2 租约
客户端需要向三个副本写入数据。在并发的情况下,也会有多个客户端同时向三个副本写入数据。GFS需要一条规则来管理这些数据的写入。简单来讲,这条规则就是每个chunk都只有一个副本来管理多个客户端的并发写入。也就是说,对于一个chunk,master会将一个块租约(chunk lease)授予其中一个副本,由具有租约的副本来管理所有要写入这个chunk的数据。这个具有租约的副本称为首要副本(primary replica)。首要副本之外的其他副本称为次要副本(secondary replica)。
2.2.3 变更及变更次序
对文件的写入称为变更(mutation)。首要副本管理所有客户端的并发请求,让所有的请求按照一定的顺序用到chunk上,这个顺序称为变更次序(mutation order)。变更包括两种,即前面讲过的write操作和record append操作。接下来介绍GFS基本变更流程,write操作就是按照这个基本变更流程进行的,而record append操作则在这个基本变更流程中多出一些特殊的处理。
1.基本变更流程
图2.2描述了GFS基本变更流程。
图2.2 GFS基本变更流程(此图参考GFS的论文[1])
整个写入过程包括以下7个步骤。
(1)当客户端要进行一次写入时,它会询问master哪个chunkserver持有这个chunk的租约,以及其他副本的位置。如果没有副本持有这个chunk的租约,那么master会挑选一个副本,通知这个副本它持有租约。
(2)master回复客户端,告诉客户端首要副本的位置和所有次要副本的位置。客户端联系首要副本,如果首要副本无响应,或者回复客户端它不是首要副本,则客户端会重新联系master。
(3)客户端向所有的副本以任意的顺序推送数据。每个chunkserver都会将这些数据缓存在缓冲区中。
(4)当所有的副本都回复已经收到数据后,客户端会发送一个写入请求(write request)给首要副本,在这个请求中标识了之前写入的数据。首要副本收到写入请求后,会给这次写入分配一个连续串行的编号,然后它会按照这个编号的顺序,将数据写入本地磁盘中。
(5)首要副本将这个带有编号的写入请求转发给次要副本,次要副本也会按照编号的顺序,将数据写入本地,并且回复首要副本数据写入成功。
(6)当首要副本收到所有次要副本的回复后,说明这次写入操作成功。
(7)首要副本回复客户端写入成功。在任意一个副本上遇到的任意错误,都会告知客户端写入失败。
2.原子记录追加
record append这个接口在论文[1]中被称为原子记录追加(atomic record append),它也遵循基本变更流程,但有一些附加的逻辑。客户端把要写入的数据(这里称为记录,record)推送给所有的副本,如果record推送成功,则客户端会发送请求给首要副本。首要副本收到写入请求后,会检查把这个record追加到尾部会不会超出chunk的边界,如果超出边界,那么它会把chunk剩余的空间填充满(这里填充什么并不重要,后面的2.4节会解释这个填充操作),并且让次要副本做相同的事情,然后再告知客户端这次写入应该在下一个chunk上重试。如果这个record适合chunk剩余的空间,那么首要副本会把它追加到尾部,并且告知次要副本写入record在同样的位置,最后通知客户端操作成功。