好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Namenode的介绍

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的相关源码。

 

 http://www.cnblogs.com/ggjucheng/archive/2013/02/04/2889386.html

分类:  hadoop

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Namenode的介绍的详细内容...

  阅读:42次

上一篇: dreamhappy博客索引

下一篇:asp.net