3.2 HDFS的写流程细节
详细的写流程分为4个步骤:打开文件、管道(pipeline)写入、上报block状态和关闭文件。下面分别介绍这4个步骤,并讲解DN的定期上报。
3.2.1 打开文件
在打开文件这个步骤中,客户端向NN发送打开文件的请求,请求中包含文件路径和文件名。NN为该客户端在这个文件上发放一个租约(lease)。
与GFS不同,HDFS的租约是发放给打开某个文件的客户端的,而GFS的租约是发放给首要副本的。其他客户端在其后要求打开同一个文件时,会被NN拒绝,从而保证只有一个客户端可以写入数据。在不出现故障的情况下,租约机制能够保证只有一个客户端写入数据,此外别无他法。
3.2.2 pipeline写入
文件打开后,开始pipeline写入的步骤,采用pipeline的方式将数据写入三个副本中。每个block都会建立一个pipeline。
每个pipeline都需要经历三个阶段:建立(setup)pipeline阶段、输送数据(data streaming)阶段和关闭(close)pipeline阶段。
1.建立pipeline阶段
建立pipeline分为两种情况。
● 如果建立pipeline的目的是在HDFS中新建一个文件,则NN新建一个block,并且为这个block选择三个DN来存储它的三个副本(称为create block)。NN会为这个block生成一个新代戳(generation stamp)。我们可以认为代戳是一个递增的数值。
● 如果建立pipeline的目的是为了追加写入数据而打开一个文件,则NN会把这个文件的最后一个block的副本所在的DN返回给客户端(称为append block)。NN会加大这个block的代戳(可以理解为把代戳的数值加1),即赋予一个更大的代戳,即表明这个block已经进入下一代。
不管是新建文件写入数据还是打开文件追加写入数据,当一个block写满数据后,客户端都会要求NN再创建一个新block。
客户端发送建立pipeline的请求给三个DN,当它收到三个DN的成功回复后,pipeline即建立成功。
2.输送数据阶段
成功建立后的pipeline如图3.2所示。
图3.2 HDFS的pipeline(此图参考HDFS设计文档[2])
在输送数据阶段,客户端会将要写入的数据先发送给DN0,由DN0将数据发送给DN1,DN1收到数据后,再将数据发送给DN2。
在pipeline建立后,客户端将要发送的数据分成多个包(packet),按顺序发送每个包,包发出后,不必等待包的回复即可发送下一个包,如图3.3所示。
图3.3 HDFS的pipeline传输数据(此图参考HDFS设计文档[2])
3.关闭pipeline阶段
在发送完所有的数据,并且收到所有包的回复之后,客户端发送一个关闭请求,关闭这个pipeline。
3.2.3 上报block状态
当客户端要求NN创建一个block,或者打开一个已存在的block追加数据时,这个block在NN上的状态被标记为UnderConstruction,表示该block处于建立状态或者追加状态。
客户端在DN上创建这个block的副本,并且为该block创建pipeline,这时这个副本的状态被标记为rbw(replica being written to),表示该副本处于建立状态(即调用create后)或者追加状态(即调用append后)。
当客户端向这个block中写完数据,将该block的pipeline关闭后,副本的状态会变为finalized,表示该副本已经完成数据写入。
DN和客户端都会向NN上报这个block的finalized状态。如果NN收到客户端的上报信息,则会将这个block的状态标记为Committed。当NN收到DN的上报信息后,它会把这个block的状态标记为Complete。
这里需要注意HDFS对block状态和副本状态的命名规则,block状态的命名规则是首字母大写,而副本状态的命名规则是字母全部小写。
3.2.4 关闭文件
如果没有数据被继续写入文件中,则客户端会向NN发起关闭文件的请求,NN会检查所有block的状态。如果所有block的状态都为Complete,则关闭文件;如果存在状态不是Complete的block,则等待DN上报状态,直到至少收到一个DN上报状态,block状态被标记为Complete。
3.2.5 DN定期上报信息
在上面的写入过程中,客户端和DN会主动向NN上报自己的状态,除此文外,DN还会定期上报信息,来注册自己并且发送自己的block的副本状态。