ClientProtocol负责完成HDFS Client与NameNode之间的交互。本文主要分析一下create方法的具体实现。Create方法在namespace中创建一条entry,实际就是在命名空间中创建一个文件。该方法在命名空间中创建的文件是一个空文件,并且,一旦创建完成,该文件对于其他客户端就是可见的和可用的(visible and available)。
首先让我们思考一下,根据我们对HDFS和其他文件系统的了解,在HDFS中创建一个文件应该经历怎样的过程呢?下面是我自己的理解:首先,我们应该检查该文件在整个文件系统中是否已经存在。如果存在,我们是不是应该覆盖原来的文件。然后,检查该文件的父目录是否都存在。如果父目录不齐全,是不是应该将其所有的父目录进行创建。最后在整个文件系统中创建相应的INodeFile和INodeDirectory。我们知道,在HDFS当中,对于文件系统的修改不做持久性存储,而是在启动的时候通过合并edits和fsimage文件来进行创建的。因此,在创建文件的时候,我们需要修改log文件,即edits文件,将本次文件的创建保存到其中。从我自己的考虑就是这些,下面看一下HDFS NameNode中具体是如何实现的。create函数的声明如下:
public void create(String src, FsPermission masked, String clientName,
boolean overwrite, boolean createParent, short replication,
long blockSize) throws IOException;
create方法的具体实现是通过namesystem.startFile(...)方法来实现的。startFile方法主要有两部分功能,一部分是通过调用startFileInternal来完成文件的创建;另一部分,通过调用getEditLog().logSync()方法来完成日志的更新。
我们首先看一下startFileInternal方法。该方法首先进行一系列的检查工作,首先检查NameNode是否处于安全模式(在安全模式下,NameNode在等待DataNode汇报保存在DataNode上的blocks,以便整个文件系统能够使用)。然后检查src是否是个合法的路径,HDFS的路径都是绝对路径,src必须以斜杠/开头,并且不能包含.或者..等符号。然后,HDFS还要检查src是否已经以目录的方式存在,如果已经存在并且是个目录,那么是不允许进行文件的创建的。然后还要进行必要的权限检查。最后,如果createParent为false,即不需要创建父目录,还需要检查父目录是否都存在。这些所有的检查完成后:
通过INode myFile = dir.getFileINode(src)来获取src所代表的文件,注意此时myFile可能为null也有可能是非null。获得myFile后,通过方法
recoverLeaseInternal(myFile, src, holder, clientMachine, false)来进行Lease(租约),即文件写锁的判断。对于Lease,在本文后面的附件中有简要的介绍。如果myFile为非null,并且myFile isUnderConstruction(即myFile处于正在构建之中),此时需要判断是不是有别的节点也在进行src的创建工作。首先,判断该client是否已经拥有src上的Lease,如果拥有,则说明该client原来已经创建了myFile,不能够重复创建。然后检查myFile原来client上的Lease,如果没有找到也会抛出异常(因为,按照常理,myFile正在处于构建之中,其原始的client必然会拥有myFile的文件写锁,但此时没有,所以抛出异常)。如果myFile已经拥有一个Lease,并且该Lease不是想要创建src的Client所拥有,那么就进行Lease恢复。完成这些操作后,就进行src文件的创建。在创建的时候还需要判断是不是append,如果是,还需要确定src已经存在并且不是目录;还需要判断是不是需要overwrite,如果是还需要删除src。Append比较简单,我们主要看一下新文件的创建,新文件的创建主要是通过INodeFileUnderConstruction newNode = dir.addFile(src,permissions, replication, blockSize, holder,clientMachine, clientNode, genstamp)来完成的。在该方法中完成INodeFile的创建,并保存到rootDir当中。最后通过fsImage.getEditLog().logOpenFile(path, newNode)完成日志的创建工作。
PS:HDFS Lease简介
Lease可以认为是一个文件写锁,当客户端需要写文件的时候,它需要申请一个Lease,NameNode负责记录那个文件上有Lease,Lease的客户是谁,超时时间(分布式处理的一种常用技术)等,所有这些工作由下面3个类完成。至于租约过期NameNode需要采取什么动作,并不是这部分code要完成的功能。
LeaseManager管理着系统中的所有Lease,同时,LeaseManager有一个线程Monitor,用于检查是否有Lease到期。
一个租约由一个holder(客户端名),lastUpdate(上次更新时间)和paths(该客户端操作的文件集合)构成。了解了这些属性,相关的方法就很好理解了。LeaseManager的方法也就很好理解,就是对Lease进行操作。注意,LeaseManager的addLease并没有检查文件上是否已经有Lease,这个是由LeaseManager的调用者来保证的,这使LeaseManager跟简单。内部类Monitor通过对Lease的最后跟新时间来检测Lease是否过期,如果过期,简单调用FSNamesystem的internalReleaseLease方法。