- hdfs为什么会做block recovery
说HDFS的block recovery,其实就是Namenode认为该block的状态需要发生一些变化,其原信息和数据都需要做一些相应的调整(或恢复),原信息的调整在namenode上,而由于数据本身是存储在datanode的磁盘上的,所以数据本身的调整其实是由datanode来完成。
那么,为什么Namenode会认为某个block的状态“需要发生一些改变呢”?这就要从代码中实际触发block recovery的入口着手。实际上,在namenode中,namenode并不是直接触发block recovery的,因为要对block做recovery操作,刚才上面提过,实际上涉及到两个部分的修改:
- block原信息的修改 —— 这部分的修改在namenode中直接就可以完成
- block数据本身的修改 —— 由于存储block实际上是datanode,所以,namenode就需要“告诉”datanode,让datanode来完成其上该block的recovery操作
所以,实际上namenode是要向datanode“发送block recovery命令”来达到上述第二个修改,也就是block数据本身的修改的目的。
而“命令”是如何发送给datanode的呢?这就涉及到HDFS中namenode和datanode的通信机制。我们都知道在HDFS中,datanode是会每隔几秒钟向namenode定期的发送心跳,namenode通过监控每隔datanode的两次心跳间隔来判断该datanode是否还活着,超过一定的时间间隔(默认10分30秒),就会认为该datanode已经死掉了。但是很多人不知道的是,datanode每次心跳发送到namenode,namenode是会返回给datanode一个“命令集(cmds)”的,这个命令集就是namenode需要datanode执行的某些操作,比如
- 将该datanode上的某个block拷贝到其他datanode上去的DNA_TRANSFER命令
- 将该datanode上的某个block从物理磁盘上删除的DNA_TRANSFER命令
- 停止该datanode的DNA_SHUTDOWN命令
- 对某个block进行block recovery的DNA_RECOVERBLOCK命令
- …...
从上可以看出,从heartbeat的返回命令集中,就包括了对某个block进行recovery的命令。所以,datanode某个block进行recovery操作的动作,实际上是来自namenode的指令。也就是说,namenode认为这个block需要做recovery了,并且这个block在某几个datanode上保存,那么namenode就会在这几个datanode的heartbeat发送过来后,给这几个datanode返回指令集,指令集中就包括对这个block进行recovery的指令。于是datanode接受到这个指令后,对block进行数据本身的recovery操作。
明白了datanode是如何会做block的recovery操作后,剩下的问题就还剩下两个了:
- namenode为什么会“认为某个block需要做recovery操作”?
- datanode在recovery一个block的时候,实际上到底对这个block做了些什么?
- namenode为什么会“认为某个block需要做recovery操作”?(什么情况下会做block recovery)
这个问题其实就是namenode对block做recovery的入口问题。什么情况下namenode会认为某个block需要进行recovery操作呢?从namenode的代码内部实现层层挖下去,就会发现,其实入口只有一个,就是namenode在对某个文件的lease进行release的时候。
于是,新的问题又来了,什么叫做“对某个文件的Lease进行release”?由于篇幅原因,这里不打算深入介绍namenode中的Lease原理和实现机制,只是简单的做一个简介:Namenode中的所谓Lease,其实就是namenode中用来标识某个Client端对HDFS中的某个文件正在进行写入操作的一个写锁。由于HDFS是一次写入,多次读取的系统,不允许对文件内容进行modify(0.20开始HDFS支持对文件的append,但仍然不支持对文件中间的某些内容进行修改),也不允许多个用户(客户端)对同一个文件同时进行修改,所以,通一时间,只能有一个客户端对一个文件的最后一个block进行写入,而如果其他客户端想在同一时间对该文件进行写入,就是不允许的。这个排他的机制,就是利用Lease来保证的。也就是说,某个客户端要对文件进行写入,必须先申请到该文件的Lease,一旦申请到后,就会允许对该文件的block进行数据写入,而如果有其他客户端已经持有这个文件的Lease,就不能再写入了。
知道了什么是Lease以后,再来解释什么叫做“对一个Lease进行release”。刚才有提到,一个Lease实际上相当于HDFS中一个文件的写锁,对应一个客户端。那么,如果一个客户端(假设叫ClientA)在持有某个文件的Lease情况下,客户端在写入数据过程中发生宕机,或者其他事故,导致无法继续对文件进行写入,会产生什么情况呢?这种情况下,由于该文件的Lease是由namenode来维护的,也就是说,此时namenode认为该文件正在被ClientA持有,所以namenode就不允许其他client对文件进行写入,但此时ClientA已经挂了,但namenode不知道,这就会导致其他所有的Client都无法对文件进行写入了。这其实是不对的。所以,namenode中对某个client对应某个文件的Lease是有一个限期的,一旦过了这个限期,该Lease没有发生任何改变(比如更新时间),没有写入任何数据,那么namenode就认为该lease对应的客户端发生了异常,需要在namenode端对这个Lease进行释放,一遍其他的client能够对文件进行写入操作。这个过程就叫做”对一个Lease进行release“操作。
至此,就明白了,其实namenode中,对block做recovery的入口只有一个,就是namenode对某个Lease进行释放的时候触发的。该函数调用在 NameNode.FSNamesystem.internalReleaseLeaseOne(Lease, filePath)。
- datanode在recovery一个block的时候,实际上到底对这个block做了些什么?(datanode做block recovery的详细过程)
接下来的问题是:datanode接收到namenode发送来的block recovery命令后,会做一些什么?
datanode在接收到block recovery命令后(通常接收到这命令的都是文件最后一个block对应的datanode targets数组中的primary datanode),就会对这个block进行真正的recovery操作。具体的recovery操作流程如下:
1, 由于block recovery 是由primary datanode发起,但该recovery操作需要在三个datanode上对该block进行操作(假设文件副本为3),所以primary datanode接收到命令的时候同时还收到了该block的targets datanode数组(其中就包括该datanode自身)
3, start block recovery指令会在datanode的磁盘中找到该block的物理块,并确认该block对应的验证信息和meta信息正确,并返回一个BlockRecord对象,表示这个block正在被recovery。
4,对每个BlockRecord,查看keepLength标志位是否为true,如果为true,则只recovery blocksize 跟 namenode中记录的blocksize一致的block,否则全部都算。
5, 对每个物理块,一旦真正开始recovery操作,则进行如下操作:在该datanode上找到该block,同时找到这个block对应的meta文件(每一个block都对应一个meta文件,用来记录该block的验证码等原信息),更新该block的stamp号(表示该block已经被修改过一次),如果需要recovery成的block的size 小于实际的block的size,则将实际的block截断成其需要的大小,并更新meta文件和验证信息。
6, 最后,primary datanode向namenode汇报本次recovery block的信息,如新的block stamp变成了多少,block size被修改成了多少等,namenoe相应的更新这些信息。