社区版Hadoop 1.0.0中对HDFS append操作进行了重新设计和实现,解决了之前append操作实现的很多缺陷。下图是对HDFS append操作详细的一般分析,该操作涉及到client,namenode和datanode的很多交付,可见其复杂性。
从Client角度来看,append一个文件首先需要调用DistributedFileSystem的append操作,该操作会返回一个FSDataOutputStream的流对象out,Client要向该文件append数据,可以调用out.write写入数据,最后调用out.close进行关闭。但是其内部的交互流程比较复杂,下面我们从上述三个client端的调用来进行逐一深入分析:
1. client调用fs的append操作:
1.1 首先调用namenode的getFileInfo RPC调用,获取该文件的信息
1.2 调用namenode的appendFile RPC调用,进行lease的检查(是否可以append,如果已经被别的client在append了,就不能再进行append),将该文件从INodeFile转换从INodeFileUnderConstruction,并且将该文件的最后一个Block以LocatedBlock的对象返回给Client。如果没有则返回null。注意,上述的流程图表示的是要append的文件之前已经成功关闭并且是一个INodeFile类型文件,否则需要判断拥有该文件append权限的client的lease是否过期,如果过期则进行lease recover操作并由namenode发起block recover操作来关闭文件。
1.3 如果上面的调用返回的LocatedBlock不为null,则在Client中创建Outputstream时调用processDatanodeError方法进行block recover操作。具体是从LocatedBlock中获取该block位于哪些datanodes上(这里假设是Datanode1, Datanode2和Datanode3),然后从这些Datanode中选取一个primaryDatanode,由该datanode(这里假设是datanode1)进行block recover的操作。在进行block recover的操作中,首先datanode1协调其他两个datanode进行startBlockRecovery操作,目的是强制终止该block file的写线程,然后调用syncBlock操作,在syncBlock操作中由datanode1调用namenode的nextGenerationStampForBlock操作获取新的GS,datanode1再协调相关datanode进行updateBlock操作,即将本地的block file更新长度和GS,最后datanode1向namenode汇报commitBlockSynchronization,至此该block recover完毕。另外,上面提到由namenode发起的block recover操作,其实流程跟由primary datanode发起的类似,只是参数有些区别而已。
2.向流对象out写数据:
2.1 在1.3调用processDatanodeError时也创建了写block的pipeline,通过该pipeline,client将数据写入pipeline中的datanodes中,这个操作是有datanode的dataxceiver.writeBlock来执行。
2.2 如果一个block写满了,Client会关闭pipeline,Datanode会把写入的block通过blockReceived汇报给namenode。然后client调用namenode的addBlock(getAdditionalBlock)获得新的block的信息(包括目标datanodes),client获得这些信息后再次建立pipeline,并写入,如此反复。注意,getAdditionalBlock会判断它前面的前面的block是否有datanode汇报上来了,如果没有,则会返回给client NotReplicatedYetException异常。
3.关闭流对象:
3.1 client关闭OutputStream时将buffer中的剩余数据通过flush的方式写入到datanode中,并标识为该block结束,datanode获取这些信息后,向namenode.blockReceived汇报block的接受信息。
3.2 client再调用namenode的completeFile,通知namenode该文件可以关闭,在completeFile函数中,namenode会检查该文件所有的block是否都至少有一个datanode汇报上来了,如果没有,则返回client继续等待的标识,如果是则将该文件的lease删除并将该文件从INodeFileUnderConstruction变回为INodeFile。