软柿子都捏完了,我们开始啃硬骨头。前面已经分析过getBlockLocations,create,append,setReplication,setPermission和setOwner,接下来我们继续回来讨论和文件内容相关的操作。
publicvoid abandonBlock(Block b, String src,String holder
) throwsIOException;
abandonBlock用于放弃一个数据块。普通的文件系统中并没有“放弃”操作,HDFS出现放弃数据块的原因,如下图所示。当客户端通过其他操作(如下面要介绍的addBlock方法)获取LocatedBlock后,可以打开到一个block的输出流,由于从DataNode出错到NameNode发现这个信息,需要有一段时间(NameNode长时间收到DataNode心跳),打开输出流可能出错,这时客户端可以向NameNode请求放弃这个数据块。
abandonBlock的处理不是很复杂,首先检查租约(调用checkLease方法。block对应的文件存在,文件处于构造状态,租约拥有者匹配),如果通过检查,调用FSDirectory的removeBlock,从INodeFileUnderConstruction/BlocksMap/CorruptReplicasMap中删除block,然后通过logOpenFile()记录变化(logOpenFile真是万能啊)。
publicLocatedBlock addBlock(String src, String clientName) throwsIOException;
写HDFS的文件时,如果数据块被写满,客户端可以通过addBlock创建新的数据块。具体的创建工作由FSNamesystem的getAdditionalBlock方法完成,当然上来就是一通检查(是否安全模式,命名/存储空间限额,租约,数据块副本数,保证DataNode已经上报数据块状态),然后通过ReplicationTargetChooser,选择复制的目标(如果目标数不够副本数,又是一个异常),然后,就可以分配数据块了。allocateBlock创建一个新的Block对象,然后调用addBlock,检查参数后把数据块加到BlocksMap对象和对应的INodeFile对象中。allocateBlock返回后,getAdditionalBlock还会继续更新一些需要记录的信息,最后返回一个新构造的LocatedBlock。
publicboolean complete(String src, StringclientName) throws IOException;
当客户端完成对数据块的写操作后,调用complete完成写操作。方法complete如果返回是false,那么,客户端需要继续调用complete方法。
FSNamesystem的同名方法调用completeFileInternal,它会:
l 检查环境;
l 获取src对应的INode;
l 如果INode存在,并且处于构造状态,获取数据块;
l 如果获取数据块返回空,返回结果CompleteFileStatus.OPERATION_FAILED,FSNamesystem的complete会抛异常返回;
l 如果上报文件完成的DataNode数不够系统最小的副本数,返回STILL_WAITING;
l 调用finalizeINodeFileUnderConstruction;
l 返回成功COMPLETE_SUCCESS
其中,对finalizeINodeFileUnderConstruction的处理包括:
l 释放租约;
l 将对应的INodeFileUnderConstruction对象转换为INodeFile对象,并在FSDirectory进行替换;
l 调用FSDirectory.closeFile关闭文件,其中会写日志logCloseFile(path, file)。
l 检查副本数,如果副本数小于INodeFile中的目标数,那么添加数据块复制任务。
我们可以看到,complete一个文件还是比较复杂的,需要释放很多的资源。
publicvoid reportBadBlocks(LocatedBlock[] blocks) throwsIOException;
调用reportBadBlocks的地方比较多,客户端可能调用,DataNode上也可能调用。
由于上报的是个数组,reportBadBlocks会循环处理,调用FSNamesystem的markBlockAsCorrupt方法。markBlockAsCorrupt方法需要两个参数,blk(数据块)和dn(所在的DataNode信息)。如果系统目前副本数大于要求,那么直接调用invalidateBlock方法。
方法invalidateBlock很简单,在检查完系统环境以后,先调用addToInvalidates方法往FSNamesystem.recentInvalidateSets添加一项,然后调用removeStoredBlock方法。
removeStoredBlock被多个方法调用,它会执行:
l 从BlocksMap中删除记录removeNode(block,node);
l 如果目前系统中还有其他副本,调用decrementSafeBlockCount(可能的调整安全模式参数)和updateNeededReplications(跟新可能存在的block复制信息,例如,现在系统中需要复制1个数据块,那么更新后,需要复制2个数据块);
l 如果目前系统中有多余数据块等待删除(在excessReplicateMap中),那么移除对应记录;
l 删除在CorruptReplicasMap中的记录(可能有)。
removeStoredBlock其实也是涉及了多处表操作,包括BlocksMap,excessReplicateMap和CorruptReplicasMap。
我们回到markBlockAsCorrupt,如果系统目前副本数小于要求,那么很显然,我们需要对数据块进行复制。首先将现在的数据块加入到CorruptReplicasMap中,然后调用updateNeededReplications,跟新复制信息。
markBlockAsCorrupt这个流程太复杂了,我们还是画个图吧:
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。