HDFS读写流程分析+源码解读

HDFS读写流程分析

结合《Hadoop权威指南》 + Hadoop源码 + 《Hadoop 2.X HDFS源码剖析》,对HDFS读写流程和源代码做了总结

内容提要

  • HDFS读写流程
  • 源代码分析和真实生产环境debug案例

1. HDFS读流程

《Hadoop权威指南》:

HDFS读操作,一般是客户端通过RPC 调用namenode以确定问价按块起始位置,对于每一个块,namenode返回保存该块副本的datanode地址(返回结果具有优先顺序),客户端通过DistributedFileSystem返回的FSDataInputStream对象,调用read方法将数据从datanode传回客户端。

几点说明:

  • 节点之间的通信协议
    client/namenode/datanode之间的互动,轻量级的都是rpc方式,比如索取数据块地址,上报失效数据块,删除数据块等等。client/datanode之间互传数据,则是用基于TCP的流式接口。namenode和备用namenode之间用的是HTTP流式接口。

  • 几个计量单位
    block : 128MB datanode数据块的大小
    packet : 64KB tcp传输的基本单位
    chunk: 512Byte tcp传输数据校验的基本单位
    在这里插入图片描述

流程

  1. 客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
  2. 客户端挑选网络距离最近的一台DataNode服务器,请求读取数据。
  3. DataNode传输数据给客户端
  4. 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件

2. HDFS写流程

  1. 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
  2. NameNode返回是否可以上传。
  3. 客户端请求第一个 Block上传到哪几个DataNode服务器上。
  4. NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
  5. 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
  6. dn1、dn2、dn3逐级应答客户端。
  7. 客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
  8. 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
    在这里插入图片描述

3. 读流程源码分析

这是一段朴实无华的文件从HDFS下载到本地的客户端代码,既然是文件传输到本地,中间的copyToLocalFile方法里面必然包含读的逻辑,debug大法一步一步看

public class HDFS_read {
    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
        Configuration conf = new Configuration();
        FileSystem fs  = FileSystem.get(new URI("hdfs://hadoop102:8020"),conf,"red");
        fs.copyToLocalFile(false,new Path("/b.txt"),new Path("E:\\b.txt"),true);
        fs.close();
    }
}

2.1 FileSystem的创建

  • 这步通过用户的配置文件生成相应的文件系统

step1 :get方法进入首先读取kerberos认证缓存得到UserGroupInformation
step2 : 创建FileSystem的核心逻辑FileSystem的静态方法get

关键步骤注释写在代码中

public static FileSystem get(URI uri, Configuration conf) throws IOException {
    //对HDFS文件系统,scheme是hdfs,对本地文件系统,scheme是file,来源于用户自己输入的URI
    String scheme = uri.getScheme();
    //authority 是namenode的域名和端口号 hadoop102:8020
    String authority = uri.getAuthority();
	//如果没指定某些信息,就去conf里面找,再没有就用FileSystem默认的
    if (scheme == null && authority == null) {     // use default FS
      return get(conf);
    }
    if (scheme != null && authority == null) {     // no authority
      URI defaultUri = getDefaultUri(conf);
      if (scheme.equals(defaultUri.getScheme())    // if scheme matches default
          && defaultUri.getAuthority() != null) {  // & default has authority
        return get(defaultUri, conf);              // return default
      }
    }

	//这步挺有意思拼接好disableCacheName后去conf里面看看用户是否指定
    String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme);
    if (conf.getBoolean(disableCacheName, false)) {
      LOGGER.debug("Bypassing cache to create filesystem {}", uri);
      return createFileSystem(uri, conf);
    }

    //去缓存中查看,没有就新建FileSystem 实例返回
    return CACHE.get(uri, conf);
  }

拓展案例
disableCacheName:fs.hdfs.impl.disable.cache相关真实案例,和上面源码有关值得一看
链接

再来个hive相关的
链接

2.2 fs.copyToLocalFile

在这里插入图片描述
转到FileUtil 的copy方法

in = srcFS.open(src);
out = dstFS.create(dst, overwrite);
IOUtils.copyBytes(in, out, conf, true);

open调用dfs的open得到FSInputStream,里面包裹着DFSInputStream
open调用localFS的create得到FSOutputStream,里面包裹着DFSInputStream
FSInputStream和FSOutputStream对接传输数据

dfs.open(getPathName(p), bufferSize, verifyChecksum);

DFSInputStream构建之前调用了getLocatedBlocks,得到了数据块的位置。这个位置的用clientprotocal 客户端rpc调用的namenode方法返回块信息(块信息按照距离客户端的网络距离排序了)

public DFSInputStream open(String src, int buffersize, boolean verifyChecksum)
      throws IOException {
    checkOpen();
    //    Get block info from namenode
    try (TraceScope ignored = newPathTraceScope("newDFSInputStream", src)) {
      LocatedBlocks locatedBlocks = getLocatedBlocks(src, 0);
      return openInternal(locatedBlocks, src, verifyChecksum);
    }
  }

3. 写流程源码分析

这是一段朴实无华的向HDFS写文件的代码

public class HDFS_write {
    public static void main(String[] args) throws IOException {
        System.setProperty("HADOOP_USER_NAME","root");
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://hadoop102:8020");
        FileSystem fs = FileSystem.get(conf);
        fs.copyFromLocalFile(false,true,new Path("E:\\b.txt"),new Path("/b.txt"));
    }
}

写方法重点看下 dstFS.create(dst, overwrite)

在create中最终找到新建DFSOutputStream的方法,里面又新建了一个streamer
在这里插入图片描述
这个streamer继承了线程,然后启动起来了
在这里插入图片描述
streamer其实就是干活的

streamer 的dataQueue数据队列存放着写入的数据

streamer 的ackQueue数据队列存放着确认数据,收到datanode的确认信息后才会清空

从队列拿到packet

one = dataQueue.getFirst();

采用pipeline模式发送数据

setPipeline(nextBlockOutputStream());
initDataStreaming();

最终closeInternal方法清空dataQueue和ackQueue

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 HDFS 1 1.1 HDFS概述 1 1.1.1 HDFS体系结构 1 1.1.2 HDFS基本概念 2 1.2 HDFS通信协议 4 1.2.1 Hadoop RPC接口 4 1.2.2 流式接口 20 1.3 HDFS主要流程 22 1.3.1 HDFS客户端读流程 22 1.3.2 HDFS客户端写流程 24 1.3.3 HDFS客户端追加写流程 25 1.3.4 Datanode启动、心跳以及执行名字节点指令流程 26 1.3.5 HA切换流程 27 第2章 Hadoop RPC 29 2.1 概述 29 2.1.1 RPC框架概述 29 2.1.2 Hadoop RPC框架概述 30 2.2 Hadoop RPC的使用 36 2.2.1 Hadoop RPC使用概述 36 2.2.2 定义RPC协议 40 2.2.3 客户端获取Proxy对象 45 2.2.4 服务器获取Server对象 54 2.3 Hadoop RPC实现 63 2.3.1 RPC类实现 63 2.3.2 Client类实现 64 2.3.3 Server类实现 76 第3章 Namenode(名字节点) 88 3.1 文件系统树 88 3.1.1 INode相关类 89 3.1.2 Feature相关类 102 3.1.3 FSEditLog类 117 3.1.4 FSImage类 138 3.1.5 FSDirectory类 158 3.2 数据块管理 162 3.2.1 Block、Replica、BlocksMap 162 3.2.2 数据块副本状态 167 3.2.3 BlockManager类(done) 177 3.3 数据节点管理 211 3.3.1 DatanodeDescriptor 212 3.3.2 DatanodeStorageInfo 214 3.3.3 DatanodeManager 217 3.4 租约管理 233 3.4.1 LeaseManager.Lease 233 3.4.2 LeaseManager 234 3.5 缓存管理 246 3.5.1 缓存概念 247 3.5.2 缓存管理命令 247 3.5.3 HDFS集中式缓存架构 247 3.5.4 CacheManager类实现 248 3.5.5 CacheReplicationMonitor 250 3.6 ClientProtocol实现 251 3.6.1 创建文件 251 3.6.2 追加写文件 254 3.6.3 创建新的数据块 257 3.6.4 放弃数据块 265 3.6.5 关闭文件 266 3.7 Namenode的启动和停止 268 3.7.1 安全模式 268 3.7.2 HDFS High Availability 276 3.7.3 名字节点的启动 301 3.7.4 名字节点的停止 306 第4章 Datanode(数据节点) 307 4.1 Datanode逻辑结构 307 4.1.1 HDFS 1.X架构 307 4.1.2 HDFS Federation 308 4.1.3 Datanode逻辑结构 310 4.2 Datanode存储 312 4.2.1 Datanode升级机制 312 4.2.2 Datanode磁盘存储结构 315 4.2.3 DataStorage实现 317 4.3 文件系统数据集 334 4.3.1 Datanode上数据块副本的状态 335 4.3.2 BlockPoolSlice实现 335 4.3.3 FsVolumeImpl实现 342 4.3.4 FsVolumeList实现 345 4.3.5 FsDatasetImpl实现 348 4.4 BlockPoolManager 375 4.4.1 BPServiceActor实现 376 4.4.2 BPOfferService实现 389 4.4.3 BlockPoolManager实现 396 4.5 流式接口 398 4.5.1 DataTransferProtocol定义 398 4.5.2 Sender和Receiver 399 4.5.3 DataXceiverServer 403 4.5.4 DataXceiver 406 4.5.5 读数据 408 4.5.6 写数据(done) 423 4.5.7 数据块替换、数据块拷贝和读数据块校验 437 4.5.8 短路读操作 437 4.6 数据块扫描器 437 4.6.1 DataBlockScanner实现 438 4.6.2 BlockPoolSliceScanner实现 439 4.7 DirectoryScanner 442 4.8 DataNode类的实现 443 4.8.1 DataNode的启动 444 4.8.2 DataNode的关闭 446 第5章 HDFS客户端 447 5.1 DFSClient实现 447 5.1.1 构造方法 448 5.1.2 关闭方法 449 5.1.3 文件系统管理与配置方法 450 5.1.4 HDFS文件与操作方法 451 5.1.5 HDFS文件读写方法 452 5.2 文件读操作与输入流 452 5.2.1 打开文件 452 5.2.2 读操作――DFSInputStream实现 461 5.3 文件短路读操作 481 5.3.1 短路读共享内存 482 5.3.2 DataTransferProtocol 484 5.3.3 DFSClient短路读操作流程 488 5.3.4 Datanode短路读操作流程 509 5.4 文件写操作与输出流 512 5.4.1 创建文件 512 5.4.2 写操作――DFSOutputStream实现 516 5.4.3 追加写操作 543 5.4.4 租约相关 546 5.4.5 关闭输出流 548 5.5 HDFS常用工具 549 5.5.1 FsShell实现 550 5.5.2 DFSAdmin实现 552

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值