SecondaryNameNode对NameNode的checkpoint流程的源码分析

来源:互联网 发布:scala java 编辑:程序博客网 时间:2024/06/04 12:56

SecondaryNameNode有两个作用,一是镜像备份,二是日志与镜像的定期合并。两个过程同时进行,称为checkpoint。

1.首先分析SecondaryNameNode的创建过程,主要的初始化逻辑在SecondaryNameNode.initialize(Configuration conf)方法中:

 1.1)从配置文件中获取参数"dfs.namenode.servicerpc-address"的值(是NameNode的地址),若此地址为空,则获取"dfs.namenode.rpc-address"的值,若仍然为空,则取"fs.default.name"的值作为缺省值;
 1.2)根据上一步得到的地址通过【RPC.waitForProxy(....)】创建NameNode对象,并赋值给SecondaryNameNode.namenode变量;
 1.3)获取参数"fs.checkpoint.dir"值(为checkpoint目录),缺省为"/tmp/hadoop/dfs/namesecondary",对N个目录初始化File对象,并赋值给SecondaryNameNode.checkpointDirs:Collection<File>对象;
 1.4)获取参数"fs.checkpoint.edits.dir"值(为checkpoint的edit文件目录),缺省为"/tmp/hadoop/dfs/namesecondary",对N个目录初始化File对象,并赋值给SecondaryNameNode.checkpointEditsDirs:Collection<File>对象;
 1.5)【new CheckpointStorage()】初始化CheckpointStorage对象并赋值给SecondaryNameNode.checkpointImage变量,此类继承FSImage类;
 1.6)【CheckpointStorage.recoverCreate(Collection<File> checkpointDirs, Collection<File> checkpointEditsDirs)】,根据checkpointDirs和checkpointEditsDirs来设置(Storage)FSImage.storageDirs队列的值,逻辑与创建NameNode中第二.3.2步一样;
 1.7)获取参数"fs.checkpoint.period"和"fs.checkpoint.size",分别表示checkpoint周期和checkpoint时edit文件的大小;赋值给SecondaryNameNode.checkpointPeriod和SecondaryNameNode.checkpointSize变量;

 1.8)初始化HttpServer,赋值给SecondaryNameNode.infoServer变量并开启此服务:此服务注册了一个Servlet:httpServer.addInternalServlet("getimage", "/getimage",GetImageServlet.class, true, SecurityUtil.useKsslAuth());注册GetImageServlet;


SecondaryNameNode对NameNode的checkpoint流程如下图所示:


下面根据源码对此流程进行详细分析:
2.然后分析SecondaryNameNode处理checkpoint的逻辑;在SecondaryNameNode的run()方法中,调用dowork()方法循环地完成主要的工作:
 2.1)通过RPC调用NameNode的getEditLogSize()方法,NameNode端调用namesystem.getEditLogSize()方法,内部调用FSEditLog.getEditLogSize()方法计算edit log的大小size;
 2.2)如果size大于配置文件中参数“fs.checkpoint.size”的值(检查点)或者上次检测的时间距离此刻已经大于配置文件中参数“fs.checkpoint.period”的值,则执行SecondaryNameNode.doCheckpoint()方法:
    2.2.1)调用startCheckpoint()方法,初始化工作环境:
         1)对${fs.checkpoint.dir}和${fs.checkpoint.edits.dir}路径解锁;
         2)关闭edit文件对应的FSEditLog.editStreams:EditLogOutputStream流;
         3)【checkpointImage.recoverCreate(checkpointDirs, checkpointEditsDirs)】:根据checkpointDirs和checkpointEditsDirs来设置(Storage)FSImage.storageDirs队列的值,并检查所有${fs.checkpoint.dir}目录下文件的一致性,逻辑与创建NameNode过程中的五.2.4.2.2步骤一样;
         4)【checkpointImage.startCheckpoint()】在${fs.checkpoint.dir}目录下创建current目录;其中,若${fs.checkpoint.dir}目录下存在VERSION文件,则必须保证存在current文件且不能有lastcheckpoint.tmp文件,并将current文件更名为lastcheckpoint.tmp,然后创建current目录;若没有VERSION文件,则直接创建current目录;
    2.2.2)【namenode.rollEditLog()】内部调用FSNamesystem.rollEditLog()方法;作用:SecondaryNameNode通知NameNode准备提交edits文件,此时在主NameNode节点所有${dfs.name.dir}目录下新建edits.new文件,在NameNode端的具体步骤如下:
         1)判断edits.new文件是否存在,若已经存在,则直接返回;  
         2)【FSImage.restoreStorageDirs()】在NameNode端初始化FSDirectory对象时,读取参数"dfs.namenode.name.dir.restore"表示是否缓存删除的文件,缺省为false,若为false,则此方法不做任何操作直接返回,若为true,且FSImage.removedStorageDirs:List<StorageDirectory>集合不为空,则将current中的fsimage和edit文件恢复到removedStorageDirs集合中的StorageDirectory指定的目录下,并将此StorageDirectory对象从removedStorageDirs集合中删除;最后将StorageDirectory对象添加到Storage.storageDirs中;
         3)创建一个edits.new文件的File对象,并以此File对象初始化EditLogFileOutputStream对象,存入FSEditLog.editStreams:ArrayList<EditLogOutputStream>中;在NameNode端后续的操作日志将记录到edits.new文件中;
         4)对于在上一步初始化EditLogFileOutputStream对象过程中抛出异常的,首先调用【FSEditLog.removeEditsForStorageDir(StorageDirectory sd)】,若此抛异常的StorageDirectory对象的EditLogFileOutputStream对象在FSEditLog.editStreams集合中(比较集合中editStreams流对应的祖父目录的路径是否与StorageDirectory对象的根路径相同),则删除掉;然后调用【FSImage.updateRemovedDirs(StorageDirectory sd)】将此StorageDirectory对象添加到removedStorageDirs集合中;
    2.2.3)【SecondaryNameNode.downloadCheckpointFiles(CheckpointSignature sig)】SecondaryNameNode通过http get方式获取NameNode的fsimage与edits文件,具体实现如下:
        背景: NameNode在创建时,在NameNode.startHttpServer(Configuration conf) 中调用 httpServer.addInternalServlet("getimage", "/getimage",GetImageServlet.class, true, SecurityUtil.useKsslAuth());注册GetImageServlet;用于SecondaryNameNode从NameNode上获取fsimage和edit文件;
         1)通过SecondaryNameNode.checkpointImage.getImageFiles()获取用于写入FSImage文件File对象;通过SecondaryNameNode.checkpointImage.getEditsFiles()获取用于写入edits文件File对象;此两个文件分别在SecondaryNameNode的${fs.checkpoint.dir}/current和${fs.checkpoint.edits.dir}/current目录中;
         2)【TransferFsImage.getFileClient(String fsName, String id, File[] localPath, boolean getChecksum)】SecondaryNameNode通过HTTP GET方式向NameNode获取FSImage和Edits文件,并分别写入上述两个File对象中;
    2.2.4)【SecondaryNameNode.doMerge(CheckpointSignature sig)】SecondaryNameNode开始合并从NameNode上获取的fsimage和edit文件,在内存中生成最新的目录树结构并在${fs.checkpoint.dir}/previous.checkpoint目录下生成一个新的fsimage文件:
         1)调用FSImage.loadFSImage(File curFile)方法加载FSImage文件;
         2)调用FSImage.loadFSEdits(StorageDirectory sd,MetaRecoveryContext recovery)方法,内部调用FSEditLog.loadFSEdits(EditLogInputStream edits, int
 tolerationLength, MetaRecoveryContext recovery) 加载edits文件内容,其实就是在此方法中将日志记录的操作内容重新执行一遍,让操作后的结合添加到目录树结构中;
         3)【FSImage.saveNamespace(boolean renewCheckpointTime=false)】根据上两步中生成在内存中的目录树结构生存新的fsimage文件,具体逻辑如下:
            a)调用FSImage.moveCurrent(StorageDirectory sd)方法,将current目录中的内容移到lastcheckpoint.tmp中,并重新创建current目录;
            b)调用FSImage.saveCurrent(StorageDirectory sd)方法,将内存中的目录树结构写入current目录中的fsimage文件中;内部调用FSImage.saveFSImage(File newFile)方法;
            c)调用FSImage.saveCurrent(StorageDirectory sd)方法创建一个空的edits文件;
            d)调用FSImage.moveLastCheckpoint(StorageDirectory sd)将第a步中lastcheckpoint.tmp目录的内容移到previous.checkpoint目录中;
    2.2.5)【SecondaryNameNode.putFSImage(CheckpointSignature sig)】,作用:SecondaryNameNode通知NameNode可以从SecondaryNameNode上以http get方式获取第2.2.4步中生成的新fsimage文件了;
         1)调用TransferFsImage.getFileClient(String fsName,String id, File[] localPath, boolean getChecksum)方法通知主NameNode从SecondaryNameNode获取最新的fsimage文件,其中将SecondaryNameNode的machine/port等信息发送给主NameNode;
         2)主NameNode检查到SecondaryNameNode的请求参数是putimage,则利用这些信息与SecondaryNameNode建立HTTP连接,获取fsimage信息;
         3)主NameNode获取到最新的FSImage文件后,生成fsimage.ckpt文件;
       备注:NameNode和Secondary NameNode间数据的通信,使用的是HTTP协议,HTTP的容器用的是jetty,TransferFsImage是文件传输的辅助类。都注册了一个Servlet容器GetImageServlet;Secondary NameNode发送一个HTTP请求到NameNode,启动NameNode上一个HTTP客户端到Secondary NameNode上去下载FSImage,下载需要的一些信息,都放在向NameNode的HTTP请求中。
    2.2.6)【NameNode.rollFsImage()】内部调用流程FSNamesystem.rollFSImage() --->FSImage.rollFSImage()
         1)检查NameNode端的edits.new文件是否存在,不存在则抛异常;
         2)检查每个${dfs.name.dir}/current目录下fsimage.ckpt文件是否存在,不存在则抛异常;
         3)【editLog.purgeEditLog()】,将每个${dfs.name.dir}/current目录下edits.new文件更名为edits:
            a)将edits.new文件更名为edits;然后删除edits文件;并从Storage.storageDirs中删除此StorageDirectory对象;
            b)从FSEditLog.editStreams中删除旧edits文件的输出流,并重新打开新edits文件的输出流;
         4)将fsimage.ckpt文件更名为fsimage,然后删除当前的fsimage文件;并从Storage.storageDirs中删除此StorageDirectory对象;
         5)若Storage.storageDirs中还存在StorageDirectory对象即认为是有旧的fsimage和edits文件,删除这些文件;
    2.2.7)【checkpointImage.endCheckpoint()】将Storage.storageDirs中存储的所有StorageDirectory对象对应的路径下的lastcheckpoint.tmp目录的内容移到previous.checkpoint目录中;此操作在第2.2.4步已经执行,为何还执行一遍呢?
 2.3)第2.2步执行完毕之后,更新SecondaryNameNode.lastCheckpointTime,即最近的checkpoint时间;
 2.4)此次SecondaryNameNode执行的checkpoint操作结束,后续周期性地执行第2.1至2.3步进行checkpoint检查;

原创粉丝点击