• HDFS namenode源码分析


    Namenode的介绍

    一个典型的HDFS系统包括一个NameNode和多个DataNode。

    NameNode作为HDFS中文件目录和文件分配的管理者,它保存的最重要信息,就是下面两个映射:

    文件名=>数据块

    数据块=>DataNode列表

    其中,文件名=>数据块保存在磁盘上(持久化);但NameNode上不保存数据块=>DataNode列表,该列表是通过DataNode上报建立起来的。

    NameNode启动流程

    在命令行启动namenode的方法是:bin/hadoop namenode

    查看bin/hadoop脚本,可以看到最后执行的java类是:org.apache.hadoop.hdfs.server.namenode.NameNode

    NameNode的代码骨架如下:

    复制代码
    public class NameNode implements ClientProtocol, DatanodeProtocol,NamenodeProtocol {
      //操作hdfs文件系统的类
      public FSNamesystem namesystem;
    
      /** RPC服务器,DFSClient,DataNode和Namenode通信都要通过它 */
      private Server server;
    
      /** httpServer,平时我们在浏览器查看的hdfs的web管理控制台,就是通过它显示的,它包装了一个内嵌的jetty */
      private HttpServer httpServer;
    
      public static NameNode createNameNode(String argv[], 
                                     Configuration conf) throws IOException {
        ...
        StartupOption startOpt = parseArguments(argv);
        ...
        switch (startOpt) {
          case FORMAT: //首次启动namenode要格式化,或者是重新初始化namenode
            boolean aborted = format(conf, true);
            System.exit(aborted ? 1 : 0);
          case FINALIZE: //完成升级hadoop,删除备份
            aborted = finalize(conf, true);
            System.exit(aborted ? 1 : 0);
          default:
        }
        ...
        //创建NameNode对象,接着会执行initialize方法初始化
        NameNode namenode = new NameNode(conf);
        return namenode;
      }
    
     private void initialize(Configuration conf) throws IOException {
         ...
         //从fsimage和edits log加载元数据
         this.namesystem = new FSNamesystem(this, conf);
        ....
        //创建RPCServer,默认的rpc线程数是10,默认端口是8020
        this.server = RPC.getServer(this, socAddr.getHostName(),
            socAddr.getPort(), handlerCount, false, conf, namesystem
            .getDelegationTokenSecretManager());    
        startHttpServer(conf);//启动http服务器,启动后可以通过http://namenode:50070 访问hdfs的管理页面
        ....
        this.server.start();  //启动RPC server   
        ....
        startTrashEmptier(conf);//启动回收站清理线程,将过期的已删除文件,真正删除。
      }
    
      public static void main(String argv[]) throws Exception {
        try {
          ...
          NameNode namenode = createNameNode(argv, null);
          if (namenode != null)
            namenode.join();
        } 
        ...
      }
    }
    复制代码

    NameNode的启动流程最复杂的就是FSNamesystem的初始化了,这个类是NameNode启动的核心逻辑,而其他启动逻辑都比较好懂。可以自行查看代码。

    org.apache.hadoop.hdfs.server.namenode.FSNamesystem具备了Namenode所提供基本服务的基础上,也可以料想到它实现的复杂性。

    FSNamesystem的分析

    FSNamesystem是文件系统命名空间系统类,它的骨架成员如下:

    复制代码
    public class FSNamesystem {
    
      //存储文件树
      public FSDirectory dir;
    
    //BlocksMap类维护块(Block)到其元数据的映射表,元数据信息包括块所属的inode、存储块的Datanode。
    
      final BlocksMap blocksMap = new BlocksMap(DEFAULT_INITIAL_MAP_CAPACITY,DEFAULT_MAP_LOAD_FACTOR);
    //失效块的映射表。
    
      public CorruptReplicasMap corruptReplicas = new CorruptReplicasMap();
      //datanode到块的映射表
      NavigableMap<String, DatanodeDescriptor> datanodeMap = new TreeMap<String, DatanodeDescriptor>();
     //datanodeMap的子集,只包含认为存活的DatanodeDescriptor,HeartbeatMonitor会定期清除过期的元素
      ArrayList<DatanodeDescriptor> heartbeats = new ArrayList<DatanodeDescriptor>();
    
      //描述某些块的副本数量不足块的实体类,而且,对于块设定了优先级,通过一个优先级队列来管理块副本不足的块的集合。
      private UnderReplicatedBlocks neededReplications = new UnderReplicatedBlocks();
    
      //描述当前尚未完成块副本复制的块的列表。
      private PendingReplicationBlocks pendingReplications;
    
      //对文件的租约进行管理。
      public LeaseManager leaseManager = new LeaseManager(this); 
    
      Daemon hbthread = null;   // 周期性地调用FSNamesystem类定义的heartbeatCheck方法,来监视Datanode结点发送的心跳状态信息,并做出处理
      public Daemon lmthread = null;   // LeaseMonitor thread
      Daemon smmthread = null;  // 用来周期性地检查是否达到离开安全模式的条件,因此,该线程必须在进入安全模式之后启动(也就是达到threshold)。
      public Daemon replthread = null;  // 周期性调用两个方法:计算块副本数量,以制定计划并调度Datanode处理 ;处理未完成块的流水线复制的副本  
    
      private ReplicationMonitor replmon = null; // Replication metrics
       
      //用来保存Datanode结点的主机 -> DatanodeDescriptor数组的映射
      private Host2NodesMap host2DataNodeMap = new Host2NodesMap(); 
    
    
      //表示一个具有树状网络拓扑结构的计算机集群,例如,一个集群可能由多个数据中心(Data Center)组成,在这些数据中心分布着为计算需求而设置的很多计算机的机架(Rack)。
      NetworkTopology clusterMap = new NetworkTopology();
    
      //该接口是一个支持插件的定义,通过插件定义DNS-name/IP-address -> RackID之间转换的解析器。
      private DNSToSwitchMapping dnsToSwitchMapping;
    
      //对指定的块副本的存放位置进行定位选择的实现类。
      ReplicationTargetChooser replicator;
      //用来跟踪Datanode的,哪些Datanode允许连接到Namenode,哪些不能够连接到Namenode,都在该类中指定的列表中记录着
     private HostsFileReader hostsReader; 
    }
    复制代码

    FSNamesystem的更多分析,参考 http://blog.csdn.net/shirdrn/article/details/4610578 

    FSNamesystem初始化的代码骨架:

    复制代码
    private void initialize(NameNode nn, Configuration conf) throws IOException {
        ...
        this.dir = new FSDirectory(this, conf);
        ...
        //从fsimage和edits加载元数据信息
        this.dir.loadFSImage(getNamespaceDirs(conf),
                             getNamespaceEditsDirs(conf), startOpt);
        ...
        this.hbthread = new Daemon(new HeartbeatMonitor()); //监视Datanode结点发送的心跳状态信息的后台线程
        this.lmthread = new Daemon(leaseManager.new Monitor());//对文件的租约进行管理后台线程。
        this.replmon = new ReplicationMonitor();
        this.replthread = new Daemon(replmon);//处理未完成块的流水线复制的副本
        hbthread.start();
        lmthread.start();
        replthread.start();
        //从配置文件读取datanode的黑白名单
        this.hostsReader = new HostsFileReader(conf.get("dfs.hosts",""),
                                               conf.get("dfs.hosts.exclude",""));
        //处理退役节点,一般会把退役节点的块做迁移
        this.dnthread = new Daemon(new DecommissionManager(this).new Monitor(
            conf.getInt("dfs.namenode.decommission.interval", 30),
            conf.getInt("dfs.namenode.decommission.nodes.per.interval", 5)));
        dnthread.start();
    
        this.dnsToSwitchMapping = ReflectionUtils.newInstance(
            conf.getClass("topology.node.switch.mapping.impl", ScriptBasedMapping.class,
                DNSToSwitchMapping.class), conf);
        
        if (dnsToSwitchMapping instanceof CachedDNSToSwitchMapping) {
          dnsToSwitchMapping.resolve(new ArrayList<String>(hostsReader.getHosts()));
        }
        ...
      }
    复制代码

    下面重点介绍成员FSDirectory类,FSNamesystem的对于元数据操作文件,就是通过它完成。

    FSDirectory的分析

    FSDirectory保存文件名到文件块的映射,并且把hdfs的修改写入到磁盘。

    要介绍FSDirectory,就要了解几个类。

    INode抽象类

    该类是一个保存在内存中的file/block层次结构,一个基本的INode包含了文件和目录inode的通用域(Field),如名字,父目录,修改时间,访问时间,权限 。

    INodeDirectory类

    INodeDirectory类继承自INode,它表示目录,可以想象得到,作为一个目录,应该提供从目录中检索得到指定的INode的操作。

    INodeFile类

    INodeFile类继承自INode,表示文件,正好与INodeDirectory相对应。它包含了文件对应的块信息,块大小,副本数。一个INodeFile类实例是不持有任何客户端或者Datanode信息的,就是一个基本的实在的文件。

    INodeFileUnderConstruction类

    因为在HDFS集群中需要执行计算任务,这要涉及到块的复制等操作,而某些块需要由Namenode调度分派给指定的进程去执行,这就需要一种实体类,既能够包含INodeFile的基本信息,又能够包含与在该INodeFile上执行操作的进程,所以,Hadoop实现了一个INodeFileUnderConstruction类,并在INodeFile类中实现了由INodeFile到INodeFileUnderConstruction的转换。

    一个INodeFileUnderConstruction文件具有持有操作该文件的进程(客户端)的一些信息,如果客户端进程同时也是HDFS集群中Datanode,它就能够根据租约的有效性来执行与该文件相关的操作,例如复制等。

    FSDirectory类

    到了这里,我们可以介绍FSDirectory类了。它是用来存储文件系统目录的状态。它处理向磁盘中写入或加载数据,并且对目录中的数据发生的改变记录到日志中。它保存了一个最新的filename->blockset的映射表,并且将它写入到磁盘中。它的主要功能实现是成员FSImage fsImage完成。

    它的核心属性如下:

    class FSDirectory implements FSConstants, Closeable {
      final INodeDirectoryWithQuota rootDir;// 具有配额限制的目录INodeDirectory,这里即是hdfs的根目录,但根目录不做配额验证
      FSImage fsImage;  // FSImage映像,管理元数据的序列化和反序列化
    }

    它的初始化代码如下:

    复制代码
       FSDirectory(FSNamesystem ns, Configuration conf) {
        this(new FSImage(), ns, conf);
        ...
      }
    
      FSDirectory(FSImage fsImage, FSNamesystem ns, Configuration conf) {
        rootDir = new INodeDirectoryWithQuota(INodeDirectory.ROOT_NAME,
            ns.createFsOwnerPermissions(new FsPermission((short)0755)),
            Integer.MAX_VALUE, -1);
        this.fsImage = fsImage;
        ....
        namesystem = ns;
        ....
      }
    
      //FSNamesystem在初始化完FSDirectory dir成员,会调用loadFSImage方法,从fsimage和edits加载元数据信息
      void loadFSImage(Collection<File> dataDirs,Collection<File> editsDirs,StartupOption startOpt) throws IOException {
        // format before starting up if requested
        if (startOpt == StartupOption.FORMAT) {// 如果启动选项类型为FORMAT(格式化),在启动之前需要进行格式化  
          fsImage.setStorageDirectories(dataDirs, editsDirs);// 设置FSImage映像文件文件的存储目录:${dfs.name.dir},默认是/tmp/hadoop/dfs/name,是一个目录数组。
          fsImage.format();// 对FSImage执行格式化操作  
          startOpt = StartupOption.REGULAR;
        }
        try {
          if (fsImage.recoverTransitionRead(dataDirs, editsDirs, startOpt)) { // 根据启动选项及其对应存储目录(${dfs.name.dir}),分析存储目录,必要的话从先前的事务恢复过来  
            fsImage.saveNamespace(true);
          }
          FSEditLog editLog = fsImage.getEditLog();
          assert editLog != null : "editLog must be initialized";
          if (!editLog.isOpen())
            editLog.open();
          fsImage.setCheckpointDirectories(null, null);
        } 
        ...
      }
    复制代码

    通过loadFSImage方法,我们可以看到加载一个FSImage映像的过程:首先需要对内存中的FSImage对象进行格式化;然后从将指定存储目录中的EditLog日志文件作用到格式化完成的FSImage内存映像上;最后需要再创建一个空的EditLog日志准备记录对命名空间进行修改的操作,以备检查点进程根据需要将EditLog内容作用到FSImage映像上,保持FSImage总是最新的,保证EditLog与FSImage同步。 

    FSDirectory的更多分析参考 http://blog.csdn.net/shirdrn/article/details/4631518

    总结

    上面将了namenode相关的核心类的成员和初始化流程,这里总结下namenode的代码调用逻辑:

    hdfs的目录和文件的创建,删除,还有文件的读写,追加,都是客户端通过rpc,调用namenode的接口。

    接着namenode调用成员FSNamesystem namesystem完成文件的操作,namesystem会做租约的管理,网络拓扑的控制,文件权限的控制等。

    接着namesystem调用成员FSDirectory dir操作,dir会做文件名到文件块的映射管理。

    接着dir调用成员FSImage fsImage操作,fsImage会hdfs的所有变化,追加写入了EditLog,做持久化。

    Secondrary Namenoe会定时(默认是一个小时)把namenode的EditLog和fsimage合并为一个fsimage,减少EditLog的文件大小。

    本文只讲解namenode核心类的职责和调用逻辑,细节请自行查看hadoop的相关源码。

  • 相关阅读:
    [置顶] Android中使用sqlite3操作SQLite
    我们不应歧视任何语言,她们都是萌娘!(有图有真相)
    一牛人博客导航
    机器学习&数据挖掘笔记_14(GMM-HMM语音识别简单理解)
    机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)
    机器学习、深度学习、数据挖掘各种资源整理
    MLP 之手写数字识别
    卷积神经网络CNN 手写数字识别
    支持向量机SVM 简要推导过程
    23种设计模式
  • 原文地址:https://www.cnblogs.com/xiaohuangya/p/3515139.html
Copyright © 2020-2023  润新知