zoukankan      html  css  js  c++  java
  • HDFS读文件详解

    HDFS读文件详解

    通过对客户端简单读取数据的源码(见图3.1)的执行进行跟踪,可以窥探到客户端是如何读取到数据的。

    图3.1 客户端简单读取数据的源码

    下面开始解释第5行到第12代码:

    第5行:根据文件的名字fileName构造一个Path类的对象path。

    第6行:初始化一个Configuration变量conf。

    第7行:根据path的成员函数getFileSystem()传入参数conf获得文件系统hdfs变量。

    以上三行根据文件名,得到一个FileSystem类的对象hdfs,其实hdfs是FileSystem子类DistributedFileSystem的一个对象。此处hdfs为何为DistributedFileSystem一个对象,由于和需求无关,故不作深入解释,你只需知道hdfs是根据conf变量和文件名fileName决定的就可以了。

    第8行:文件系统变量hdfs调用open()方法传入参数path得到一个输入流。

    此处对open方法进行详细解释。

     

    3.1 hdfs.open()详解

    3.1.1 FileSystem.open(path)

    Hdfs.open(path)是调用基类FileSystem的open(path)函数,图3.2为FileSystem的open()函数:

    图3.2 FileSystem的open(path)函数

    FileSystem.open(path)又调用FileSystem的抽象方法open(path, size)。

    3.1.2 FileSystem.open(path, size)

    图3.3为FileSystem的open(path, size)函数:

     

    图3.3 FileSystem的抽象方法open(path, size)

    抽象方法的实现是在子类中实现的,故实际调用的是子类的DistributedFileSystem实现的open(path, size)方法。

    此处为何代码"FileSystem hdfs=path.getFileSystem(conf)"返回的是子类DistributedFileSystem的一个实例,这是根据path和conf共同决定的,具体分析,详见以后的分析,此处先不详述。

    3.1.3 DistributedFileSystemd.open(path, size)

    图3.4为DistributedFileSystem.open(path, size)的实现:

    图3.4 DistributedFileSystemd的open(path, size)

    图3.4中188行的dfs为DFSClient类的一个实例,DFSClient通过代理模式(用到了hadoop实现的RPC类)建立与NameNode的socket连接,具体细节不必深究,只需要知道DFSClient的功能即可。DFSClient的open(String src, int buffersize, boolean verifyChecksum, FileSystem.Statistics stats)方法返回一个DFSInputStream对象。

    3.1.4 DFSClient.open(String src, int buffersize, boolean verifyChecksum, FileSystem.Statistics stats)

    图3.5为DFSClient的open(String src, int buffersize, boolean verifyChecksum, FileSystem.Statistics stats)实现:

    图3.5 DFSClient的open()方法

    第478行检测clientRunning是否正在运行。

    第480行根据DFSInputStream的构造函数:

    DFSInputStream(String src, int buffersize, boolean verifyChecksum)构造一个DFSInputStream对象,DFSInputStream对象的构造详见3.1.5节。

    3.1.5 DFS.checkOpen()

    图3.6 DFS的checkOpen()方法

    检测clientRunning是否正在运行,没有运行则抛出异常。

    3.1.6 DFSInputStream.DFSInputStream(String src, int buffersize, boolean verifyChecksum)

    DFSInputStream的构造函数见下图3.7:

    图3. 7 DFSInputStream构造函数

    DFSInputStream构造函数的前四行(即1682、1683、1684、1685行),分别在初始化一些参数;第1686行的openInfo(),它内部建立了与NameNode 的连接,通过调用DFSClient的callGetBlockLocations(namenode, src, 0, prefetchSize)

    3.1.7 DFSInputStream.openInfo()

    DFSInputStream. openInfo()详见下图3.8:

    图3.8 DFSInputStream. openInfo()

    图3.8的1693行调用DFSClient的方法callGetBlockLocations()。

    callGetBlockLocations()函数的定义可以看出,它是调用了namenode 的getBlockLocations()方法;而namenode 的getBlockLocations()方法为:依据代理实现的对NameNode进行的远程过程调用,详见3.1.7节。

    在第一次调用openInfo()时,DFSInputStream类的成员变量locatedBlocks为NULL,故不会执行图3.8的1700到1709这段代码。

    第1710行updateBlockInfo(newInfo),在文件构造过程中,基于从datanode返回的block长度,更新最后一个block的大小。

    第1711行,将DFSClient的callGetBlockLocations(namenode, src, 0, prefetchSize)方法返回的LocatedBlock变量赋给DFSClient的成员locatedBlocks。

    3.1.8 DFSClient.callGetBlockLocations()

    DFSClient的方法callGetBlockLocations()详见图3.9:

    图3.9 DFSClient的callGetBlockLocations()

    由图3. 9中callGetBlockLocations()函数的定义可以看出,它调用了namenode 的getBlockLocations()方法;而namenode 的getBlockLocations()方法为:依据代理实现的对NameNode进行的远程过程调用。namenode变量为一个ClientProtocol对象。

    图3.10 ClientProtocol接口的getBlockLocations()

    3.1.9 DFSInputStream.updateBlockInfo(LocatedBlocks newInfo)

    图3. 11 DFSInputStream.updateBlockInfo(LocatedBlocks newInfo)

    DFSInputStream的updateBlockInfo(LocatedBlocks newInfo),在文件构造过程中,基于从datanode返回的block长度,更新最后一个block的大小。

    3.2 FSDataInputStream.read()详解

    下面通过对读文件简单例子的分析,得出最外层面向开发者的FSDataInputStream.read(byte b[], int off, int len)的本质,最后帮助修改,解决需求(指定DataNode进行数据块读取)。

    图3.12 客户端简单读取数据的源码

    由图3.12第8行:

    FSDataInputStream inputStream=hdfs.open(path);

    可以看出open()函数返回了一个FSDataInputStream流对象,然后对这个流对象就行读操作,从表象看来确实如此,其实不然。

    3.2.1 FSDataInputStream.read(byte b[], int off, int len)

    究其实现可以看出,FSDataInputStream.read(byte b[], int off, int len)调用的是其父类DataInputStream的read(byte b[], int off, int len),图3.13为FSDataInputStream的继承关系:

    图3.13 FSDataInputStream继承关系

    3.2.2 DataInputStream.read(byte b[], int off, int len)

    DataInputStream. read(byte b[], int off, int len)调用的是其成员的in的read(byte b[], int off, int len),见图3.14:

    图3.14 DataInputStream. read(byte b[], int off, int len)

    此处的read()需要特殊说明一下,in的read是从输入流in中读取长度为len的数据存放在b[] byte数组的off之后(包括off)的元素中,而不是我们通常所理解的那样。下面为英文解释:

    The first byte read is stored into element b[off], the next one into b[off+1], and so on. The number of bytes read is, at most, equal to len. Let k be the number of bytes actually read; these bytes will be stored in elements b[off] through b[off+k-1], leaving elements b[off+k] through b[off+len-1] unaffected.

    3.2.3 DataInputStream.in是什么

    DataInputStream的成员in是什么呢,我们看其父类FilterInputStream,图3.15:

    图3.15 FilterInputStream的成员变量in

        追溯到FilterInputStream类就知道了:

    FSDataInputStream.read(byte b[], int off, int len)调用其父类DataInputStream的read(byte b[], int off, int len);

    DataInputStream的read(byte b[], int off, int len)调用的是其成员变量InputStream in的read方法(byte b[], int off, int len)。

    此处又出现了一个问题,这个类型为InputStream的in(真正的输入流)是怎么传进来的。

    3.2.4 真正的输入流in

    下面通过DFSInputStream类的继承关系和hdfs.open(path)的部分过程详解真正的输入流的构造生成过程。

    图3.16 DFSInputStream类继承关系

    图3.17 hdfs.open()获得流过程

    我们由图3.17的hdfs.open()获得流过程的代码由下向上看(也可借鉴3.1.3小节)。

    第30行构造了一个DFSInputStream变量并返回(此处记为a,方便下面描述解释)。

    第15行的dfs.open(……)的返回值就是上一行所述的构造的DFSInputStream变量a。

    第14行是根据15行构造的DFSInputStream变量a作为DFSClient的内部类DFSDataInputStream的构造函数的参数,下图3.18为DFSDataInputStream的构造函数的执行过程:

    图3.18 DFSDataInputStream的构造函数的执行过程

    由图3.18可以看出,执行到最后,其实是将DFSInputStream类的对象a赋给DFSDataInputStream类的成员变量in,类DFSDataInputStream类的成员变量in为InputStream类型。

    故图3.17的第14行最后返回了一个(带有类型为DFSInputStream的输入流in的成员变量)DFSDataInputStream对象。

    图3.17的第9行返回一个DFSDataInputStream对象。

    图3.17的第8行返回一个DFSDataInputStream对象。

    故图3.17的第1行:

    FSDataInputStream inputStream=hdfs.open(path);

    返回了一个(带有类型为DFSInputStream的输入流in的成员变量)DFSDataInputStream对象,到最后返回给它的直接父类FSDataInputStream的对象。下面一节对DFSInputStream的read()方法进行分析。

    附件1

    hdfs.open(path)执行流程:

  • 相关阅读:
    linux Segmentation faults 段错误详解
    linux cut
    linux sed
    linux tr
    linux ar
    objdump--反汇编查看
    linux中dd命令
    readelf
    登录后,前端做了哪些工作,如何得知已登录?
    正向代理和反向代理?
  • 原文地址:https://www.cnblogs.com/maybob/p/3042780.html
Copyright © 2011-2022 走看看