HDFS相关源码剖析

DFSClient |  Namenode |  Datanode 源码分析顺序图:

 

DFSClient

    |-------ClientProtocol

    |-------DFSInputStream 

    |-------LocatedBlocks

    |-------BlockReader

    |-------DFSInputStream

    |-------DFSOutputStream

              |--------Packet 

    |--------pipeline

              |--------DataStreamer 

              |--------ResponseProcessor 

Datanode

    |-------BlockSender

    |-------DataXceiverServer

    |-------- BlockReceiver

    |-------DatanodeProtocol

    |-------InterDatanodeProtocol

    |-------ClientDatanodeProtocol

  

Namenode

 |-------FSNameSystem

 |-------FSDirectory

 |-------FsImage

 |-------LeaseManager

 |-------HeartbeatManager

|-------HeartbeatThread

|------Monitor

 

 

 

一、DFSClient相关体系

 

DFSClient

    |-------ClientProtocol

    |-------DFSInputStream 

    |-------LocatedBlocks

    |-------BlockReader

    |-------DFSInputStream

 

    |-------DFSOutputStream

              |--------Packet 

    |--------pipeline

              |--------DataStreamer 

              |--------ResponseProcessor 

DFSClient类的介绍源码:

* DFSClient can connect to a Hadoop Filesystem and

 * perform basic file tasks.  It uses the ClientProtocol

 * to communicate with a NameNode daemon, and connects

 * directly to DataNodes to read/write block data.

DFSClient这个类的作用是用于客户端连接Hadoop的HDFS文件系统,并在HDFS文件系统上做基本的文件操作(任务)。这个类和通过RPC机制和HDFS文件系统通信的,具体的RPC通信协议接口类是:org.apache.hadoop.hdfs.protocol.ClientProtocol。

DFSClient通过PRC和namenode节点和datanode节点进行通信以及实现在datanode上执行读/写 block的操作

 

重要方法源码:

static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,

      String src, long start, long length)

      throws IOException {

    try {

      return namenode.getBlockLocations(src, start, length);

    } catch(RemoteException re) {

      throw re.unwrapRemoteException(AccessControlException.class,

                                     FileNotFoundException.class,

                                     UnresolvedPathException.class);

    }

  }

这个函数主要是通过跟namenode的交互,来完成从namenode取得用户请求的file的元数据信息

 

ClientProtocol介绍

客户端和namenode进行rpc通信的协议接口,实现类是org.apache.hadoop.hdfs.protocol.ClientProtocol。

 

DFSClient通过RPC机制和NameNode通信并获得文件的元数据信息(数据的存放位置,校验和,等等一些关键的信息),然后DFSClient再与DataNode通信(集群中的其它机器)通过数据I/O流来获取到指定的文件信息。

 

 

DFSInputStreamBlockReader  介绍

DFSInputStream是DFSClient用于读取datanode上文件的输入流。当DFSClient向namenode发送读文件请求之后,namenode会将此file的元数据信息返回: LocatedBlocks。这个类,封装了文件块的信息(每个文件块的大小、所在的datanode节点信息)。该输入流根据这些信息找到对应的DataNode,然后调用BlockReader类的read()方法,读取datanode节点上文件块里的内容。

重要变量源码:

public class DFSInputStream {

  private final DFSClient dfsClient;

  private BlockReader blockReader = null;

  private LocatedBlocks locatedBlocks = null;

  private DatanodeInfo currentNode = null;

}

 

DFSOutputStreamPacket DataStreamerResponseProcessor介绍

DFSoutputStream类是一个输出流,用于提供客户端将本地文件(在客户端的电脑上存的文件)上传到HDFS上。当客户端发送一个

写请求后(create请求),比如上传一个大文件。首先会通过DFSclient去找namenode,namenode首先会创建对应的目录,然后返回给DSFClient输出流。拿到输出输出流之后,  根据namenode返回的块信息blocksize,将数据变成64kb的packet存储到dataqueue队列里

 

DataStreamer会从dataQueue里取出一个一个的packet进行数据传输,然后形成一个数据流管道 pipeline。然后将

数据流管道输出给管道里的第一个datanode节点。第一个datanode节点将数据流管道信息交给管道中的第二个节点。直到管道里最后一个datanode完成存储并返回ack确认后,通知resoponse线程,然后DataStreamer发送下一个packet。

 

DFSOutputStream里有2个队列和2个线程

两个队列相关代码:

dataQueue是数据队列,用于保存等待发送给datanode的数据包 

 private final LinkedList<Packet> dataQueue = new LinkedList<Packet>();

ackQueue是确认队列,保存还没有被datanode确认接收的数据包

 private final LinkedList<Packet> ackQueue = new LinkedList<Packet>();

两个线程:

streamer线程,不停的从dataQueue中取出数据包,发送给datanode 

private DataStreamer streamer = new DataStreamer();

response线程,用于接收从datanode返回的反馈信息

private ResponseProcessor response = null; 

 

在向DFSOutputStream中,写入数据(通常是byte数组)的时候,实际的传输过程是: 

1、文件数据以字节数组进行传输,每个byte[]被封装成64KB的Packet,然后扔进dataQueue中 

2、DataStreamer线程不断的从dataQueue中取出Packet,通过socket发送给datanode(向blockStream写数据)

    发送前,将当前的Packet从dataQueue中移除,并addLast进ackQueue 

3、ResponseProcessor线程从blockReplyStream中读出从datanode的反馈信息 

       反馈信息很简单,就是一个seqno,再加上每个datanode返回的标志(成功标志为  DataTransferProtocol.OP_STATUS_SUCCESS) 

       通过判断seqno(序列号,每个Packet有一个序列号),判断datanode是否接收到正确的包。 

       只有收到反馈包中的seqno与ackQueue.getFirst()的包seqno相同时,说明正确。否则可能出现了丢包的情况。 

4、如果一切OK,则从ackQueue中移出:ackQueue.removeFirst(); 说明这个Packet被datanode成功接收了。 

 

DataStreamer类重要源码:

private DataStreamer() {

      isAppend = false;

      stage = BlockConstructionStage.PIPELINE_SETUP_CREATE;

    }

在DatStreamer开始发送block时,创建pipeline数据流管道

 

 

二、Datanode相关体系

Datanode

    |-------BlockSender

    |-------DataXceiverServer

    |-------- BlockReceiver

    |-------DatanodeProtocol

    |-------InterDatanodeProtocol

    |-------ClientDatanodeProtocol

 

BlockSender介绍

1.当用户(客户端)向HDFS读取某一个文件时,客户端会根据数据所在的位置转向到具体的DataNode节点请求对应数据块的数据,此时DataNode节点会用BlockSender向该客户端发送数据;

2.当NameNode节点发现某个Block的副本不足时,它会要求某一个存储了该Block的DataNode节点向其它DataNode节点复制该Block,当然此时仍然会采用流水线的复制方式,只不过数据来源变成了一个DataNode节点;

3.HDFS开了一个调节DataNode负载均衡的工具Balancer,当它发现某一个DataNode节点存储的Block过多时,就会让这个DataNode节点转移一部分Blocks到新添加到集群的DataNode节点或者存储负载轻的DataNode节点上;

sh start-balancer.sh -t %10

百分数是磁盘使用偏差率,一般调节的范围在10%~20%间。

4.DataNode会定期利用BlockSender来检查一个Block的数据是否损坏。

 

 

DataXceiverServer介绍

 

datanode端是如何接受传来的数据文件呢?

在datanode类里,有一个线程类:

org.apache.hadoop.hdfs.server.datanode.DataXceiver。每当有client连接到datanode时,datanode会new一个DataXceiver,负责数据的传输工作。

 

在这个DataXceiver线程类里:

相关代码:

@Override

public void writeBlock(){

/** A class that receives a block and writes to its own disk, meanwhile

 * may copies it to another site. If a throttler is provided,

 * streaming throttling is also supported.

 **/
 BlockReceiver blockReceiver = null; // responsible for data handling

}

会调用 BlockReceiver 这个类,这个类是用于接收数据并写入本地磁盘,还负责将数据传输给管道里下一个datanode节点。

 

DatanodeProtocol介绍

* Protocol that a DFS datanode uses to communicate with the NameNode.

 * It's used to upload current load information and block reports.

这个类是datanode和namenode  实现RPC通信的接口协议,作用是datanode通过RPC机制连接namenode并汇报自身节点状态信息。在这个接口类里,一个非常重要的方法是:

相关代码:

public HeartbeatResponse sendHeartbeat(){

}

这个是datanode向namenode发送心跳的方法,datanode周期性通过RPC调用sendHeartbeat向namenode汇报自身的状态。

 

 

三、NameNode相关体系

Namenode

 

 |-------FSNameSystem

 |-------DFSConfigKeys

 |-------FSDirectory

 |-------FsImage

 |-------LeaseManager

 |-------HeartbeatManager

|-------HeartbeatThread

|------Monitor

 

Namenode介绍

* NameNode serves as both directory namespace manager and

 * "inode table" for the Hadoop DFS.  There is a single NameNode

 * running in any DFS deployment.  (Well, except when there

 * is a second backup/failover NameNode, or when using federated NameNodes.)

 *

 * The NameNode controls two critical tables:

 *   1)  filename->blocksequence (namespace)

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

 *

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

 * The second table is 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.

Namenode类是Hadoop DFS文件系统的管理者类,用来管理HDFS的元数据信息。实际控制的是两张表信息,分别是:
1.文件——文件块信息

2.文件块信息——存储这个文件块的机器列表信息

第一张表信息存储在namenode节点的磁盘上,并且访问是非常高效的(因为namenode在启动之后,会将数据加载到内存里供快速访问)

第二张表信息,在每次namenode重启工作后,会重新建立。(这么做目的是为了确保块信息存储的准备性,实现机制时,当namenode重启工作后,每个datanode节点通过rpc心跳向namenode汇报自身存储的文件块信息,然后namenode根据这些信息,重建第二张表信息,在此过程中,HDFS是处于安全模式的,即只能对外提供读服务。当第二表信息重建完后,确认文件块数量正确且都完整,则退出安全模式

 

实际上,namenode更多的是充当一个领导角色或更像是一个协议类,它定义了需要做哪些事,而真正干活的是FSNamesystem这个类。

在这个类里,format()这个方法我们应该会很熟悉,这是namenode的格式化方法

 

format()方法:

/** Format a new filesystem.  Destroys any filesystem that may already

   * exist at this location.  **/

  public static void format(Configuration conf) throws IOException {

    format(conf, true, true);

  }

格式化方法的定义:生成一个全新的文件系统(DFS文件系统)。并且摧毁已经存在的旧数据信息。

format()方法源码骨架:

 private static boolean format(Configuration conf, boolean force boolean isInteractive) throws IOException {

//调用Fsnamesystem,获取用户配置的元数据信息存放路径。如果不配置,则默认使用linux/tmp/hadoop/dfs/name这个目录。

//但是这个目录是临时目录,非常危险,所以需要更改。

     Collection<URI> nameDirsToFormat = FSNamesystem.getNamespaceDirs(conf);

     List<URI> editDirsToFormat =  FSNamesystem.getNamespaceEditsDirs(conf);

//然后调用FSImage类,在对应的目录下,创建新的Fsimage文件和Edits文件

    FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);

    fsImage.format(fsn, clusterId);

  }

 

 

FSimage介绍

把文件和目录的元数据信息持久化地存储到fsimage文件中,每次启动时从中将元数据加载到内存中构建目录结构树,之后的操作记录在edits 中

定期将edits与fsimage合并刷到fsimage中

loadFSImage(File curFile)用于从fsimage中读入Namenode持久化的信息。

 

HeartbeatManager介绍

* Manage the heartbeats received from datanodes.

 * The datanode list and statistics are synchronized

 * by the heartbeat manager lock.

namenode通过这个类来管理各个datanode传来的心跳。具体工作的线程是 HeartbeatThread这个线程类

 

Monitor介绍

这是HeartbeatManager类里一个私有线程类,这个类的作用是周期性检测各个Datanode的心跳,

在这个线程类里run方法里,有一个方法比较重要,就是 heartbeatCheck();这个方法的作用:

   * Check if there are any expired heartbeats, and if so,

   * whether any blocks have to be re-replicated.

   * While removing dead datanodes, make sure that only one datanode is marked

   * dead at a time within the synchronized section. Otherwise, a cascading

   * effect causes more datanodes to be declared dead.

检查是否有超时的datanode心跳。如果有的话,先判断下在这个死亡节点上是否有数据块需要进行复制。然后对这个datanode进行死亡标记。如果这个节点上有文件需要复制备份,则进行数据备份(满足一个block在HDFS上存3份),复制完后然后再删除这个死掉的datanode节点。从slaves列表中移除

 

此外,在进行死亡节点数据复制过程中,HDFS对外不提供写服务,即客户端此时是不能上传文件到HDFS系统上的。直到数据复制完成(比如一个block达到3份),最后删除此死亡节点后,才对外提供写服务。此过程中,不影响HDFS的查询文件操作

相关代码:

private class Monitor implements Runnable {

  

    @Override

    public void run() {

                heartbeatCheck();

              }

 

  }

发布了31 篇原创文章 · 获赞 13 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/laughing1997/article/details/95941603
今日推荐