_00005 Hadoop DataNode源码浅析(RPC是基础)

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


# DataNode源码分析

# datanode注释翻译

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

 * DataNode is a class (and program)that stores a set of

 * blocks for a DFS deployment.  A single deployment can

 * have one or many DataNodes.  Each DataNode communicates

 * regularly with a singleNameNode.  It also communicates

 * with client code and otherDataNodes from time to time.

 *

 * DataNodes store a series of namedblocks.  The DataNode

 * allows client code to read theseblocks, or to write new

 * block data.  The DataNode may also, in response toinstructions

 * from its NameNode, delete blocksor copy blocks to/from other

 * DataNodes.

 *

 * The DataNode maintains just onecritical table:

 *  block-> stream of bytes (of BLOCK_SIZE or less)

 *

 * This info is stored on a localdisk.  The DataNode

 * reports the table's contents tothe NameNode upon startup

 * and every so often afterwards.

 *

 * DataNodes spend their lives in anendless loop of asking

 * the NameNode for something todo.  A NameNode cannot connect

 * to a DataNode directly; aNameNode simply returns values from

 * functions invoked by a DataNode.

 *

 * DataNodes maintain an open serversocket so that client code

 * or other DataNodes can read/writedata.  The host/port for

 * this server is reported to theNameNode, which then sends that

 * information to clients or otherDataNodes that might be interested.

 *

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

# 首先看datanode结构,实现了Runnable接口(run方法)。

public class DataNode extendsConfigured

    implements InterDatanodeProtocol,ClientDatanodeProtocol, FSConstants, Runnable, DataNodeMXBean {

# 找到main方法,进入

public static void main(String args[]) {

    secureMain(args, null);

  }

# 进入scureMain

public static void secureMain(String [] args, SecureResourcesresources) {

    try {

      StringUtils.startupShutdownMessage(DataNode.class, args,LOG);

      DataNode datanode = createDataNode(args,null, resources);

      if (datanode !=null)

        datanode.join();

创建datanode跟调用datanode.join()(Java Threadjoin()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码。)

# 进入createDataNode

/** Instantiate & Start a single datanode daemon and wait for it tofinish.

   * If this thread is specifically interrupted, it will stop waiting.

   * LimitedPrivate for creating secure datanodes

   */

  public static DataNode createDataNode(Stringargs[],

            Configuration conf,SecureResources resources) throws IOException {

    DataNode dn = instantiateDataNode(args,conf, resources);

    runDatanodeDaemon(dn);

    return dn;

  }

注释说:实例化和开始一个datanode守护进程(runDatanodeDaemon(dn)),等待它完成。如果专门打断这个线程,它将停止等待。创建安全datanodes LimitedPrivate。

# startDataNode方法,一直跟进去(省略中间的代码)最后进入到这么一个主要的方法,该方法代码很多

void startDataNode(Configuration conf,

                    AbstractList<File> dataDirs, SecureResources resources

                     ) throws IOException {

第一个重要的地方:

// connect to name node

    this.namenode = (DatanodeProtocol)

      RPC.waitForProxy(DatanodeProtocol.class,

                      DatanodeProtocol.versionID,

                       nameNodeAddr,

                       conf);

在datanode中起了一个RPC的客户端,得到一个服务端的代理对象,这里被强转为DatanodeProtocol,实际上就是NameNode这个类,因为NameNode类实现了DatanodeProtocol接口,然后就可以调用NameNode里面的方法了

第二个重要的地方:

this.threadGroup =new ThreadGroup("dataXceiverServer");

    this.dataXceiverServer =new Daemon(threadGroup,

        new DataXceiverServer(ss, conf,this));

下面我们可以来开始分析DataNode上的动态行为。首先我们来分析DataXceiverServer和DataXceiver。DataNode上数据块的接受/发送并没有采用我们前面介绍的RPC机制,原因很简单,RPC是一个命令式的接口,而DataNode处理数据部分,往往是一种流式机制。DataXceiverServer和DataXceiver就是这个机制的实现。其中,DataXceiver还依赖于两个辅助类:BlockSender和BlockReceiver。  DataXceiverServer很简单,它打开一个端口,然后每接收到一个连接,就创建一个DataXceiver,服务于该连接,DataXceiver是一个线程读一次操作请求进行操作之后就返回,并记录该连接的socket,对应的实现在DataXceiverServer的run方法里。当系统关闭时,DataXceiverServer将关闭监听的socket和所有DataXceiver的socket,这样就导致了DataXceiver出错并结束线程。DataXceiverServer接受到的数据主要有操作码+操作数据+用户名。  (1)BlockSender用来发送block数据,返回给用户的是:成功与否+校验类型+实际offset(因为校验块的原因和用户请求的offset不一致)。BlockSender有配置参数corruptChecksumOk(校验数据读入出错忽略,出错用零填充),chunkOffsetOK(是否要告知实际的offset,如上所述),verifyChecksum(是否要求在把校验数据和实际数据读入包缓存中时校验数据,也就是在发送之前),向客户端传包的时候第一、二个参数为true,第三为false,为的是尽快发送数据。而用来校验已有数据时使用第一二参数为false,第三参数为true,为了及时发现错误数据。readBlock完成实际读数据的操作,比较简单。sendChunks方法中,对于客户端传包的包只有校验和而实际数据通过管道传输,具体见函数。

第三个重要的地方:

//create a servlet to serve full-file content

    InetSocketAddress infoSocAddr = DataNode.getInfoAddr(conf);

    String infoHost =infoSocAddr.getHostName();

    int tmpInfoPort = infoSocAddr.getPort();

    this.infoServer = (secureResources ==null)

       ? new HttpServer("datanode", infoHost, tmpInfoPort, tmpInfoPort == 0,

           conf, SecurityUtil.getAdminAcls(conf,DFSConfigKeys.DFS_ADMIN))

       : new HttpServer("datanode", infoHost, tmpInfoPort, tmpInfoPort == 0,

           conf, SecurityUtil.getAdminAcls(conf,DFSConfigKeys.DFS_ADMIN),

           secureResources.getListener());

这里new了一个infoServer,new HttpServer里面是一个jetty的server,就是为了向用户提供web界面的,然后再后面再启动infoServer。

第四个重要的地方:

//init ipc server

    InetSocketAddress ipcAddr =NetUtils.createSocketAddr(

        conf.get("dfs.datanode.ipc.address"));

    ipcServer = RPC.getServer(this,ipcAddr.getHostName(), ipcAddr.getPort(),

        conf.getInt("dfs.datanode.handler.count", 3),false, conf,

        blockTokenSecretManager);

这里datanode中起了一个RPC的服务端(暂时不知给谁调用的)

 

# 接下来就会调用dtanode.join()方法,datanode的run方法

/**

   * No matter what kind ofexception we get, keep retrying to offerService().

   * That's the loop that connectsto the NameNode and provides basic DataNode

   * functionality.

   *

   * Only stop when"shouldRun" is turned off (which can only happen at shutdown).

   */

  public void run() {

LOG.info(dnRegistration +"InDataNode.run, data = " +data);

 

    // start dataXceiveServer

    dataXceiverServer.start();

    ipcServer.start();

       

    while (shouldRun) {

      try {

        startDistributedUpgradeIfNeeded();

        offerService();

      } catch (Exception ex) {

        LOG.error("Exception: " + StringUtils.stringifyException(ex));

        if (shouldRun) {

          try {

            Thread.sleep(5000);

          } catch (InterruptedExceptionie) {

          }

        }

      }

    }

       

    LOG.info(dnRegistration +":Finishing DataNode in: "+data);

    shutdown();

  }

# 我们主要看offerService();这个方法

/**

   * Main loop for theDataNode.  Runs until shutdown,

   * forever calling remote NameNodefunctions.

   */

  public void offerService() throws Exception {

里面有一个死循环,主要是调用了一个方法sendHeartbeat

DatanodeCommand[] cmds = namenode.sendHeartbeat(dnRegistration,

                                                      data.getCapacity(),

                                                       data.getDfsUsed(),

                                                      data.getRemaining(),

                                                      xmitsInProgress.get(),

                                                       getXceiverCount());

          myMetrics.addHeartBeat(now() - startTime);

          //LOG.info("Just sent heartbeat, with name " + localName);

          if(!processCommand(cmds))

            continue;

这里的namenode在上面已经解释过了,其实就是DataNode,这里会调用DataNode的sendHeartbeat方法,将自己的状态作为参数(容量,空间使用了多少,剩余多少等等)传递给了namenode。然后namenode调用方法得到一些返回值给datanode,datanode处理这些命令,然后再处理这些指令processCommand(cmds),放送心跳基本分析完毕。

# 接下来看发送心跳以及处理的namenode的指令后datanode还干了些什么

DatanodeCommand cmd= namenode.blockReport(dnRegistration,

                   BlockListAsLongs.convertToArrayLongs(bReport));

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

 

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

// If we have sent the firstblock report, then wait a random

// time before we start the periodic blockreports.

如果我们把第一块报告发送了之后,在我们开始定期块报告之前会等待一个随机时间。

 

发送完毕之后NameNode会给DataNode发送一些指令,然后DataNode会处理这些指令

processCommand(cmd);

基本上也就分析完了

# DataNode启动过程分析

DataNode一启动,会对DataNode进行初始化,最终进入到startDataNode方法中,该方法

主要有4个重要的地方。

1起了一个RPC客户端,namenode,实现的接口是DataProtocol,主要是DataNode跟NameNode通信用的,DataNode将自己的一些状态(容量,使用,未使用等)告诉NameNode,然后NameNode返回DataNode一些指令,告诉DataNode要去做什么。

2dataXceiverServer的启动,是一个线程组(流服务)

3起了一个jetty服务器,提供web方式的访问

4起了一个RPC的服务端,给NameNode调用(应该是这样)

 

接着会实例化datanode和开始一个datanode守护进程(runDatanodeDaemon(dn)),然后会调用datanode的join方法,进入到run方法,这个方法将刚刚startDataNode方法中的RPC服务端开启,然后调用了一个offerService方法,里面是一个死循环,最先开始是datanode RPC远程调用namenode.sendHeartbeat方法,这里的namenode在上面已经解释过了,其实就是DataNode,这里会调用DataNode的sendHeartbeat方法,将自己的状态作为参数(容量,空间使用了多少,剩余多少等等)传递给了namenode。然后namenode调用方法得到一些返回值给datanode,datanode处理这些命令,然后再处理这些指令processCommand(cmds)。

接下来datanode会调用namenode.blockReport的方法,datanode开始扫描自己目录下所保存的所有文件块,然后通过namenode的rpc调用将这些block信息以一个long数组的方式发送给namenode,namenode在接收到一个datanode的blockReport rpc调用后,从rpc中解析出block数组,并将这些接收到的blocks插入到BlocksMap表中,由于NameNode启动时BlocksMap缺少的仅仅是每个block对应的datanode信息,而namenoe能从blocReport中获知当前blockReport上来的是哪个datanode的块信息,所以,blockReport过程实际上就是namenode在接收到块信息汇报后,填充BlocksMap中每个block对应的datanodes列表的三元组信息的过程。当所有的datanode汇报完block,namenode针对每个datanode的汇报进行过处理后,namenode的启动过程到此结束。此时BlocksMap中block->datanodes的对应关系已经初始化完毕。如果此时已经达到安全模式的退出阈值,则hdfs主动退出安全模式,开始提供服务。

调用完namenode.blockReport方法之后 ,namenode会给一些指令给datanode,然后datanode再处理这些指令。

在这个过程中是datanode主动与namenode通信,然后namenode传给datanode一些函数的返回值,告诉datanode该做什么。

DataProtocol中的注释:

* The only way aNameNode can communicate with a DataNode is by

 *returning values from thesefunctions.

 

妳那伊抹微笑

The you smile until forever 、、、、、、、、、、、、、、、、、、、、、
1 0
原创粉丝点击