_00004 Hadoop NameNode源码浅析(RPC是基础)

博文作者: 妳那伊抹微笑
个性签名: 世界上最遥远的距离不是天涯,也不是海角,而是我站在妳的面前,妳却感觉不到我的存在
技术方向: Flume+Kafka+Storm+Redis/Hbase+Hadoop+Hive+Mahout+Spark ... 云计算技术
转载声明: 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明,谢谢合作!
qq交流群: 214293307  云计算之嫣然伊笑(期待与你一起学习,共同进步)


# NameNode源码分析(RPC是基础)

# namenode注释翻译

/**********************************************************

 * NameNode servesas both directory namespace manager and

 * "inodetable" for the Hadoop DFS.  There isa single NameNode

 * running in anyDFS deployment.  (Well, except when there

 * is a secondbackup/failover NameNode.)

 *

 * The NameNodecontrols two critical tables:

 *   1) filename->blocksequence (namespace)

 *   2) block->machinelist ("inodes")

 *

 * The first tableis stored on disk and is very precious.

 * The second tableis rebuilt every time the NameNode comes

 * up.

 *

 * 'NameNode'refers to both this class as well as the 'NameNode server'.

 * The'FSNamesystem' class actually performs most of the filesystem

 * management.  The majority of the 'NameNode' class itselfis concerned

 * with exposingthe IPC interface and the http server to the outside world,

 * plus someconfiguration management.

 *

 * NameNodeimplements the ClientProtocol interface, which allows

 * clients to askfor DFS services.  ClientProtocol is not

 * designed fordirect use by authors of DFS client code. End-users

 * should insteaduse the org.apache.nutch.hadoop.fs.FileSystem class.

 *

 * NameNode alsoimplements the DatanodeProtocol interface, used by

 * DataNodeprograms that actually store DFS data blocks. These

 * methods areinvoked repeatedly and automatically by all the

 * DataNodes in aDFS deployment.

 *

 * NameNode alsoimplements the NamenodeProtocol interface, used by

 * secondarynamenodes or rebalancing processes to get partial namenode's

 * state, forexample partial blocksMap etc.

 **********************************************************/

对于HDFS来说NameNode是一个目录命名空间管理器和”inode table”,它是一个单个的NameNode运行在任何的DFS的部署环境中(好吧,除非是有第二个备份/故障转移NameNode。)

 

NameNode控制着两个关键表:

1)  filename->blocksequence (namespace)第一个表名为文件名,放的是block的顺序

2) block->machinelist ("inodes")第二个表是block,放的是block所存放的机器列表

第一个表存放在硬盘上并且非常珍贵

第二个表在每次NameNode重启的时候会被重构

 

NameNode指的是这个类以及NameNode server,FSNamesystem这个类实际上执行的大多数文件系统管理。NameNode这个类主要的工作就是关注暴露的IPC接口以及向外部世界(用户)提供一些http服务,加上一些配置管理。

 

NameNode实现了ClientProtocol接口,它允许客户请求DFS服务。ClientProtocol不是专门为直接使用DFS客户机代码的作者设计的,终端用户(程序员)应该使用FileSystem这个类。

NameNode也实现了DatanodeProtocol接口,被DataNode的程序使用去完成DFS数据块的存储。在一个DFS的环境中NameNode实现了DatanodeProtocol接口中的方法会被所有的DataNode自动重复的调用执行。

 

NameNode也实现了NamenodeProtocol接口,被secondarynamenode使用或在平衡过程的进程中得到NameNode 的部分状态,例如部分blocksMap等等

 

# 知道了RPC原理才能更好的理NameNode

# 首先看namenode类的结构,主要实现了ClientProtocol, DatanodeProtocol, NamenodeProtocol这三个接口

# 进入NameNode的源代码找到publicclass NameNodeimplements ClientProtocol, DatanodeProtocol, NamenodeProtocol,FSConstants,RefreshAuthorizationPolicyProtocol,

                                RefreshUserMappingsProtocol {

# 接下来进入main方法(由于NameNode是一个RPC的服务端,所以我们进入RPC的main方法,为了证明NameNode是一个RPC的服务端)

public static void main(String argv[]) throws Exception {

    try {

      StringUtils.startupShutdownMessage(NameNode.class,argv,LOG);

      NameNode namenode = createNameNode(argv,null);

      if (namenode != null)

        namenode.join();

    } catch (Throwable e) {

      LOG.error(StringUtils.stringifyException(e));

      System.exit(-1);

    }

  }

# 进入createNameNode方法(只看重点,会有下面这么一行)

NameNodenamenode = new NameNode(conf);

# 再点进去

public NameNode(Configuration conf) throws IOException {

    try {

      initialize(conf);

    } catch (IOException e) {

      this.stop();

      throw e;

    }

  }

# 进入initialize(conf)方法(只看重点代码)

this.namesystem =newFSNamesystem(this, conf);

 

    if (UserGroupInformation.isSecurityEnabled()){

      namesystem.activateSecretManager();

    }

// create rpc server

    InetSocketAddress dnSocketAddr =getServiceRpcServerAddress(conf);

    if (dnSocketAddr !=null) {

      int serviceHandlerCount =

        conf.getInt(DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_KEY,

                    DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_DEFAULT);

      this.serviceRpcServer = RPC.getServer(this, dnSocketAddr.getHostName(),

          dnSocketAddr.getPort(),serviceHandlerCount,

          false, conf,namesystem.getDelegationTokenSecretManager());

      this.serviceRPCAddress =this.serviceRpcServer.getListenerAddress();

      setRpcServiceServerAddress(conf);

    }

    this.server = RPC.getServer(this, socAddr.getHostName(),

        socAddr.getPort(),handlerCount, false, conf,namesystem

       .getDelegationTokenSecretManager());

 

    // The rpc-server port can be ephemeral... ensure we have the correct info

    this.serverAddress =this.server.getListenerAddress();

    FileSystem.setDefaultUri(conf,getUri(serverAddress));

    LOG.info("Namenode up at: " +this.serverAddress);

 

   

 

    startHttpServer(conf);

    this.server.start(); //start RPC server  

    if (serviceRpcServer !=null) {

      serviceRpcServer.start();     

    }

    startTrashEmptier(conf);

namesystem后面再解释(namenode的初始化,namenode启动加载fsimage以及一些配置,后面详细解释)

//create rpc server 意思就是创建 RPC服务端,也就是说NameNode是一个RPC的服务端

注意:这里不是启动了一个rpc的服务端,而是启动了两个rpc的服务端。

serviceRpcServerRPC服务器为了HDFS服务通信。备份节点(secondarynamenode),Datanodes和所有其他服务应该连接到这个服务器配置。客户应该只去调用NameNode 下的server的RPC服务端(这个是程序内部调用的)

server主要是用来给客户端调用的

# 然后再来看startHttpServer(conf);(开启一个Http的服务器)这个方法,跟进去

try {

      this.httpServer = ugi.doAs(new PrivilegedExceptionAction() {

        @Override

        public HttpServer run()throws IOException,InterruptedException {

          String infoHost =infoSocAddr.getHostName();

          int infoPort = infoSocAddr.getPort();

          httpServer = new HttpServer("hdfs", infoHost,infoPort,

              infoPort == 0, conf,

              SecurityUtil.getAdmin

# 在进入HttpServer 的构造方法,跟进new HttpServer("hdfs", infoHost, inf

public HttpServer(String name, String bindAddress, int port,

      boolean findPort, Configuration conf,AccessControlList adminsAcl)

      throws IOException {

    this(name, bindAddress, port, findPort, conf,adminsAcl,null);

  }

# 再跟进this(name, bindAddress, port, findPort, conf,adminsAcl,null);

public HttpServer(String name, String bindAddress, int port,

      boolean findPort, Configuration conf,AccessControlList adminsAcl,

      Connector connector) throws IOException{

    webServer =new Server();

    this.findPort = findPort;

    this.conf = conf;

    this.adminsAcl = adminsAcl;

 

    if(connector ==null) {

到这里就行了,把鼠标放到new Server()上面去,可以看到这么一行提示信息

org.mortbay.jetty.Server.Server()

这里额外说明一下jettyjettytomcat一样,也是一个服务器,只是更小而已,被内置到NameNode中去了。

到了这里之后就可以看出来NameNode开启了一个jetty服务,也就是可以通过浏览器访问,也就是我们经常访问的http://hadoopip:50070(这里hadoopip表示你的hadoop机器的ip地址)出现的web界面

# NameNode启动过程详细剖析

# NameNode中几个关键的数据结构

# FSImage

Namenode会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中,每次保存fsimage之后到下次保存之间的所有hdfs操作,将会记录在editlog文件中,当editlog达到一定的大小(bytes,由fs.checkpoint.size参数定义)或从上次保存过后一定时间段过后(sec,由fs.checkpoint.period参数定义),namenode会重新将内存中对整个HDFS的目录树和文件元数据刷到fsimage文件中。Namenode就是通过这种方式来保证HDFS中元数据信息的安全性。

Fsimage是一个二进制文件,当中记录了HDFS中所有文件和目录的元数据信息,在我的hadoopHDFS版中,该文件的中保存文件和目录的格式如下:

 

namenode重启加载fsimage时,就是按照如下格式协议从文件流中加载元数据信息。从fsimag的存储格式可以看出,fsimage保存有如下信息:

1.        首先是一个image head,其中包含:

a)        imgVersion(int):当前image的版本信息

b)       namespaceID(int):用来确保别的HDFSinstance中的datanode不会误连上当前NN

c)        numFiles(long):整个文件系统中包含有多少文件和目录

d)       genStamp(long):生成该image时的时间戳信息。

2.        接下来便是对每个文件或目录的源数据信息,如果是目录,则包含以下信息:

a)        path(String):该目录的路径,如”/user/build/build-index”

b)       replications(short):副本数(目录虽然没有副本,但这里记录的目录副本数也为3

c)        mtime(long):该目录的修改时间的时间戳信息

d)       atime(long):该目录的访问时间的时间戳信息

e)        blocksize(long):目录的blocksize都为0

f)        numBlocks(int):实际有多少个文件块,目录的该值都为-1,表示该item为目录

g)       nsQuota(long)namespaceQuota值,若没加Quota限制则为-1

h)       dsQuota(long)disk Quota值,若没加限制则也为-1

i)         username(String):该目录的所属用户名

j)         group(String):该目录的所属组

k)       permission(short):该目录的permission信息,如644等,有一个short来记录。

3.        若从fsimage中读到的item是一个文件,则还会额外包含如下信息:

a)        blockid(long):属于该文件的blockblockid

b)       numBytes(long):该block的大小

c)        genStamp(long):该block的时间戳

当该文件对应的numBlocks数不为1,而是大于1时,表示该文件对应有多个block信息,此时紧接在该fsimage之后的就会有多个blockidnumBytesgenStamp信息。

因此,在namenode启动时,就需要对fsimage按照如下格式进行顺序的加载,以将fsimage中记录的HDFS元数据信息加载到内存中。

# BlockMap

从以上fsimage中加载如namenode内存中的信息中可以很明显的看出,在fsimage中,并没有记录每一个block对应到哪几个datanodes的对应表信息,而只是存储了所有的关于namespace的相关信息。而真正每个block对应到datanodes列表的信息在hadoop中并没有进行持久化存储,而是在所有datanode启动时,每个datanode对本地磁盘进行扫描,将本datanode上保存的block信息汇报给namenodenamenode在接收到每个datanode的块信息汇报后,将接收到的块信息,以及其所在的datanode信息等保存在内存中。HDFS就是通过这种块信息汇报的方式来完成 block-> datanodes list的对应表构建。Datanodenamenode汇报块信息的过程叫做blockReport,而namenodeblock -> datanodeslist的对应表信息保存在一个叫BlocksMap的数据结构中。

BlocksMap的内部数据结构如下:   

 

如上图显示,BlocksMap实际上就是一个Block对象对BlockInfo对象的一个Map表,其中Block对象中只记录了blockidblock大小以及时间戳信息,这些信息在fsimage中都有记录。而BlockInfo是从Block对象继承而来,因此除了Block对象中保存的信息外,还包括代表该block所属的HDFS文件的INodeFile对象引用以及该block所属datanodes列表的信息(即上图中的DN1DN2DN3,该数据结构会在下文详述)。

因此在namenode启动并加载fsimage完成之后,实际上BlocksMap中的key,也就是Block对象都已经加载到BlocksMap中,每个key对应的value(BlockInfo)中,除了表示其所属的datanodes列表的数组为空外,其他信息也都已经成功加载。所以可以说:fsimage加载完毕后,BlocksMap中仅缺少每个块对应到其所属的datanodeslist的对应关系信息。所缺这些信息,就是通过上文提到的从各datanode接收blockReport来构建。当所有的datanode汇报给namenodeblockReport处理完毕后,BlocksMap整个结构也就构建完成。

# BlockMapdatanode列表数据结构

BlockInfo中,将该block所属的datanodes列表保存在一个Object[]数组中,但该数组不仅仅保存了datanodes列表,“。实际上该数组保存了如下信息:

 

上图表示一个block包含有三个副本,分别放置在DN1DN2DN3三个datanode上,每个datanode对应一个三元组,该三元组中的第二个元素,即上图中prev block所指的是该block在该datanode上的前一个BlockInfo引用。第三个元素,也就是上图中next Block所指的是该block在该datanode上的下一个BlockInfo引用。每个block有多少个副本,其对应的BlockInfo对象中就会有多少个这种三元组。

      Namenode采用这种结构来保存block->datanodelist的目的在于节约namenode内存。由于namenodeblock->datanodes的对应关系保存在了内存当中,随着HDFS中文件数的增加,block数也会相应的增加,namenode为了保存block->datanodes的信息已经耗费了相当多的内存,如果还像这种方式一样的保存datanode->blocklist的对应表,势必耗费更多的内存,而且在实际应用中,要查一个datanode上保存的block list的应用实际上非常的少,大部分情况下是要根据block来查datanode列表,所以namenode中通过上图的方式来保存block->datanode list的对应关系,当需要查询datanode->blocklist的对应关系时,只需要沿着该数据结构中next Block的指向关系,就能得出结果,而又无需保存datanode->blocklist在内存中。

# NameNode启动过程

# fsimage加载过程

Fsimage加载过程完成的操作主要是为了:

1.        fsimage中读取该HDFS中保存的每一个目录和每一个文件

2.        初始化每个目录和文件的元数据信息

3.        根据目录和文件的路径,构造出整个namespace在内存中的镜像

4.        如果是文件,则读取出该文件包含的所有blockid,并插入到BlocksMap中。

整个加载流程如下图所示:

 

如上图所示,namenode在加载fsimage过程其实非常简单,就是从fsimage中不停的顺序读取文件和目录的元数据信息,并在内存中构建整个namespace,同时将每个文件对应的blockid保存入BlocksMap中,此时BlocksMap中每个block对应的datanodes列表暂时为空。当fsimage加载完毕后,整个HDFS的目录结构在内存中就已经初始化完毕,所缺的就是每个文件对应的block对应的datanode列表信息。这些信息需要从datanodeRPC远程调用blockReport中获取,所以加载fsimage完毕后,namenode进程进入rpc等待状态,等待所有的datanodes发送blockReports

# blockReport阶段

每个datanode在启动时都会扫描其机器上对应保存hdfs block的目录下(dfs.data.dir)所保存的所有文件块,然后通过namenoderpc调用将这些block信息以一个long数组的方式发送给namenodenamenode在接收到一个datanodeblockReport rpc调用后,从rpc中解析出block数组,并将这些接收到的blocks插入到BlocksMap表中,由于此时BlocksMap缺少的仅仅是每个block对应的datanode信息,而namenoe能从report中获知当前report上来的是哪个datanode的块信息,所以,blockReport过程实际上就是namenode在接收到块信息汇报后,填充BlocksMap中每个block对应的datanodes列表的三元组信息的过程。其流程如下图所示:

 

当所有的datanode汇报完blocknamenode针对每个datanode的汇报进行过处理后,namenode的启动过程到此结束。此时BlocksMapblock->datanodes的对应关系已经初始化完毕。如果此时已经达到安全模式的推出阈值,则hdfs主动退出安全模式,开始提供服务。


# NameNode源码分析总结

一个hdfs的cluster包含了一个NameNode和若干个DataNode,NameNode是master,主要负责管理hdfs文件系统,具体的包括namespace管理(目录结构)和block管理(具体filename->blocksequence(namespace),block->datanode list(“inodes”))。前者是通过FSImage写入到本地文件系统中,而后者是通过每次hdfs启动时,datanode进行blockreport后在内存中重构的数据结构。在hdfs的程序代码中,namenode类其实只是一个用来被动接收调用的服务的包装,它实现了ClientProtocol接口,用来接收来自DFSClient的RPC请求;它实现了DatanodeProtocol接口,用来接收来自datanode的各种服务请求;同时还实现了NamenodeProtocol,用来提供跟SeconddaryNameNode之间的RPC的请求和通信。对以上数据结构进行维护的是hdfs中的FSNamesystem类。对于NameNode的各种请求,比如创建,修改,删除,移动,getLocations的操作,在NameNode内部都是通过FSNamesystem提供的接口对内部数据结构进行的访问。

 

NameNode是一个目录命名空间的管理器,NameNode在hdfs中只有一个。(当启动一个NameNode的时候,会产生一个锁文件,是锁住的,所以起不了第二个NameNode了)

NameNode维护这两张核心表:

       1. Filename------blocksequence (“namespace”)也就是block的顺序

       2. block------machinelist(“inodes”) 每个block的存储的机器(dataNode)列表

NameNode其实就是一个RPC的服务端,并且启动了两个RPC服务端(这里又涉及到了RPC原理了,看不懂的话就看下RPC的原理),并且还开启了一个jetty服务器,对外界提供了WEB的访问方式。




妳那伊抹微笑

The you smile until forever 、、、、、、、、、、、、、、、、、、、、、

你可能感兴趣的:(hadoop,源码分析,hadoop)