说明

除了标注之外,本文纯属原创,转载请注明出处:https://www.jianshu.com/p/ea6ef5f5b868, https://www.cnblogs.com/monkeyteng/p/10220395.html

HDFS架构简介

Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。本文基于Hadoop 2.7.3源码,分析本地文件推送(新建/追加)到的HDFS客户端逻辑。

  1. HDFS架构主要包含两种类型的节点:NameNode和DataNode。
  2. NameNode,其实就是名字节点,其功能类似于我们常用的磁盘文件系统中的inode。对于HDFS而言,NameNode相当于“目录管理器”和“inode表”。
  3. NameNode保存两类关键的映射表:
  • 名字空间表:从文件名到数据块(DataBlock)的映射,这部分数据保存在NameNode服务器的磁盘。
  • inode表:从数据块(DataBlock)到机器的映射,包括每一个数据块保存在哪一个或者哪几个机器上。这部分数据在每次重启NameNode的时候都会和DataNode通讯并重建。
  1. 对于Hadoop 2.7.3而言,一个DataBlock默认是128MB,所以一个文件可能需要N个DataBlock来存储,那么名字空间表很可能是一个文件名映射到一个DataBlock的数组。
  2. 关于这两张表如何协作定位文件:
  • 当使用文件名访问文件时,NameNode会查询名字空间表,根据这个文件名获取它所有内容对应的DataBlock列表(是不是很类似于单机磁盘的数据访问)。此时inode表会查询每一个DataBlock的信息,包括它所在的位置(DataNode的IP+端口)、DataBlock的ID和时间戳以及里面数据的长度(<=128MB)等。
  • 这个DataBlock列表返回到客户端,客户端根据每个DataBlock上的信息(线索),分别连接到每个DataNode上,获取上面存储的数据。
  1. 客户端与NameNode、NameNode与DataNode的连接,全部都是通过ProtoBuf的RPC调用来实现的。关于ProtoBuf可以参考这里。例如,下面就是追加文件的append请求的RPC协议:
//摘自hadoop-hdfs-project/hadoop-hdfs/src/main/proto/ClientNamenodeProtocol.proto

//RPC请求
rpc append(AppendRequestProto) returns(AppendResponseProto); //请求报文
message AppendRequestProto {
required string src = 1;
required string clientName = 2;
optional uint32 flag = 3; // bits set using CreateFlag
} //应答报文
message AppendResponseProto {
optional LocatedBlockProto block = 1;
optional HdfsFileStatusProto stat = 2;
}

HDFS写文件Pipeline机制

HDFS在对文件的写入方面,只允许数据追加到文件末尾,而不允许在文件中间修改文件。因为在文件中间修改文件,需要涉及文件锁、数据块之类的比较复杂的逻辑。

Hadoop的文件按照DataBlock分块,并以DataBlock为单位做冗余(负载均衡)。HDFS可以指定一个复制因子(replication),默认是保存3份,根据dfs.replication配置项配置。

下面分析HDFS写文件的Pipeline流程(蓝色线表示用于通讯,红色线表示数据的传输路线):

  • ①客户端发送请求到NameNode,请求写文件/新建数据块。
  • NameNode收到请求后,会给客户端分配一个数据块,其ID是blk_123456,并指明DataBlock各个拷贝所在的各个DataNode的IP和端口(图中是分别存在于三个DataNode中)。
  • 这一系列的DataNode称为Pipeline,也就是数据传输的管道,也就是【DataNode_1:50010, DataNode_2:50010, DataNode_3:50010】。
  • ②客户端收到数据块的信息,开始对DataNode发起写的请求,请求报文包括要写的数据块,要写的数据大小等等。请求成功后,发送数据到第一个DataNode,也就是图中的DataNode_1,在该请求中包含DataBlock各个拷贝的地址(包含DataNode2和DataNode3的地址):【DataNode_1:50010, DataNode_2:50010, DataNode_3:50010】,发送完成之后等待DataNode_1返回的ACK报文。
  • ③DataNode_1收到数据后,保存数据,并把数据发送到DataNode_2,Pipeline修改为【DataNode_2:50010, DataNode_3:50010】,发送完成之后等待DataNode_2返回的ACK报文。
  • ④DataNode_2收到数据后,把数据发送到DataNode_3,Pipeline修改为【DataNode_3:50010】,发送完成之后等待DataNode_2返回的ACK报文。
  • ⑤DataNode_3发现Pipeline中只有自己,不再有下游的DataNode节点,于是处理完成之后只需要返回ACK到Pipeline的上游节点,即DataNode_2。
  • ⑥DataNode_2收到DataNode_3的ACK,于是把ACK发送到Pipeline的上游节点,即DataNode_1。
  • ⑦DataNode_1收到DataNode_2的ACK,把ACK发送到Pipeline的上游节点,即客户端。

数据发送至此完成。

HDFS文件推送客户端

要把本地文件推送到HDFS,可以通过以下两个命令实现:

hadoop fs -appendToFile <localsrc> ... <dst>
hadoop fs -put [-f] [-p] [-l] <localsrc> ... <dst>

跟踪调用堆栈发现,这两个命令最终是调用DFSOutputStream.java中的代码实现文件的拷贝。

辅助发送的相关类和数据结构

这份代码里面包含了一些用于辅助发送的类:

  • DFSOutputStream:实现了发送数据的主流程,最主要是继承自FSOutputSummer这个虚拟类的接口方法writeChunk
  • DataStreamer:继承自Daemon的后台线程,主要实现数据的流式发送。
  • ResponseProcessor:同样继承自Daemon的后台线程,主要实现对已发送数据包的ACK报文的接收。

还有一些保存发送数据相关信息的数据结构:

  • DFSPacket:表示发送出去的一个数据包,包含相应的请求头部以及相关标志位。
  • LinkedList<DFSPacket> dataQueue:用于保存待发送的数据包。它是主线程*DFSOutputStream和发送线程DataStreamer之间生产者-消费者关系的共享数据结构。
  • LinkedList<DFSPacket> ackQueue:用于保存已经发送的数据包。发出去的数据包还要等待DataNode返回ACK才可以被认为是发送成功。它是发送线程DataStreamer与ACK接收线程ResponseProcessor之间生产者-消费者关系的共享数据结构。
  • BlockConstructionStage stage:这是一个状态变量,整个发送流程就相当于一个状态机。

看完上面的数据结构,整个数据发送流程就很明显了:

DFSOutputStream把数据组装成DFSPacket对象,放入dataQueue;然后等待发送线程DataStreamer发送到DataNode;DataStreamer发送之后,把DFSPacket对象移动到ackQueue,等待ACK线程ResponseProcessor在收到对应的ACK之后把该DFSPacket从队列移除。

下面主要分析DFSOutputStream.java这个客户端代码的执行流程。

数据发送的主要流程

  • newStreamForCreate/newStreamForAppend这两个静态函数用于创建DFSOutputStream对象。一个是用于新建文件,一个用于追加到现有的文件。两个函数主要差别在于,前者需要新建一个文件(发送create的RPC请求到NameNode),后者直接通过发送append的RPC请求到NameNode,在返回报文中获取文件最后的一个数据块并开始写入。
  • newStreamForCreate/newStreamForAppend这两个函数返回一个DFSOutputStream的对象,然后被org.apache.hadoop.io.IOUtils.copyBytes()调用DFSOutputStreamwriteChunk接口函数,把本地的数据块发送出去。下面主要看writeChunk函数。这个函数的参数主要包括数据的缓冲区、要发送的数据在DataBlock中的offset、还有数据的校验等。

HDFS深度历险 之 从客户端逻辑看HDFS写入机制的更多相关文章

  1. SSH深度历险(十一) AOP原理及相关概念学习+xml配置实例(对比注解方式的优缺点)

    接上一篇 SSH深度历险(十) AOP原理及相关概念学习+AspectJ注解方式配置spring AOP,本篇我们主要是来学习使用配置XML实现AOP 本文采用强制的CGLB代理方式 Security ...

  2. SSH深度历险(十) AOP原理及相关概念学习+AspectJ注解方式配置spring AOP

    AOP(Aspect Oriented Programming),是面向切面编程的技术.AOP基于IoC基础,是对OOP的有益补充. AOP之所以能得到广泛应用,主要是因为它将应用系统拆分分了2个部分 ...

  3. 分布式Redis深度历险-Cluster

    本文为分布式Redis深度历险系列的第三篇,主要内容为Redis的Cluster,也就是Redis集群功能. Redis集群是Redis官方提供的分布式方案,整个集群通过将所有数据分成16384个槽来 ...

  4. 分布式Redis深度历险-复制

    Redis深度历险分为两个部分,单机Redis和分布式Redis. 本文为分布式Redis深度历险系列的第一篇,主要内容为Redis的复制功能. Redis的复制功能的作用和大多数分布式存储系统一样, ...

  5. Unicode(UTF&UCS)深度历险

    Unicode(UTF&UCS)深度历险 计算机网络诞生后,大家慢慢地发现一个问题:一个字节放不下一个字符了!因为需要交流,本地化的文字需要能够被支持. 最初的字符集使用7bit来存储字符,因 ...

  6. Redis深度历险——核心原理与应用实践

    高可用架构」的各位老铁们,你们好!你是否还记得上个月发布的文章中,有两篇深入讲解Redis的文章,分别是和,广大粉丝读者们对这两篇文章整体评价颇高.而我就是这两篇文章的原创作者「老钱」(钱文品),我是 ...

  7. HDFS源码解析:教你用HDFS客户端写数据

    摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...

  8. HDFS的Java客户端操作代码(HDFS的查看、创建)

    1.HDFS的put上传文件操作的java代码: package Hdfs; import java.io.FileInputStream; import java.io.FileNotFoundEx ...

  9. 从一般分布式设计看HDFS设计思想与架构

     要想深入学习HDFS就要先了解其设计思想和架构,这样才能继续深入使用HDFS或者深入研究源代码.懂得了"所以然"才能在实际使用中灵活运用.快速解决遇到的问题.下面这篇博文我们就先 ...

随机推荐

  1. Java设计模式小议之1------- 迭代器模式

    定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 这里用一个具体的案例来说明一下迭代器模式的简单使用 我们都知道在商店中,经常要把商品放到书架上,并将商品的 ...

  2. MongoDB之基本操作与日常维护

    MongoDB基本操作 MongoDB的基本操作主要是对数据库.集合.文档的操作,包括创建数据库.删除数据库.插入文档.更改文档.删除文档.和查询文档. 操作 描述 show dbs 查看当前实例下的 ...

  3. rsync命令详解、rsync用ssh隧道方式同步

    ● rsync格式安装命令 yum install -y rsync与scp的区别:scp复制为完全覆盖,rsync为增量同步,只同步修改过的数据.rsync命令格式如下: rsync 选项 源文件 ...

  4. 巧妙地使用typora编辑有道云笔记

    设置方法 找到有道云笔记本地保存路径: 找到有道云笔记的保存的路径:启动有道云 - 设置 - 有道云笔记(本地文件) - 打开文件夹 使用typora打开有道云笔记目录: typora 菜单栏 - O ...

  5. Android 音视频同步机制

    一.概述 音视频同步(avsync),是影响多媒体应用体验质量的一个重要因素.而我们在看到音视频同步的时候,最先想到的就是对齐两者的pts,但是实际使用中的各类播放器,其音视频同步机制都比这些复杂的多 ...

  6. Servlet生命周期 、Filter生命周期、Listering(监听器)总结

    Servlet生命周期简述 (1)加载和实例化 当Servlet容器启动或客户端发送一个请求时,Servlet容器会查找内存中是否存在该Servlet实例,若存在,则直接读取该实例响应请求:如果不存在 ...

  7. ASP.NET Core Middleware 抽丝剥茧

    一. 宏观概念 ASP.NET Core Middleware是在应用程序处理管道pipeline中用于处理请求和操作响应的组件. 每个组件是pipeline 中的一环. 自行决定是否将请求传递给下一 ...

  8. 使用C#+XPath+HtmlAgilityPack轻松搞一个资源下载器

    HtmlAgilityPack简介 HtmlAgilityPack是一个开源的解析HTML元素的类库,最大的特点是可以通过XPath来解析HMTL,如果您以前用C#操作过XML,那么使用起HtmlAg ...

  9. Python爬虫入门教程 47-100 mitmproxy安装与安卓模拟器的配合使用-手机APP爬虫部分

    1. 准备下载软件 介绍一款爬虫辅助工具mitmproxy ,mitmproxy 就是用于MITM的proxy,MITM中间人攻击.说白了就是服务器和客户机中间通讯多增加了一层.跟Fiddler和Ch ...

  10. GlideNewDemo【Glide4.7.1版本的简单使用以及圆角功能】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 简单记录下Glide4.7.1版本的使用和实现圆角方案. 注意:关于详细使用请仔细阅读<官方指南>. 效果图 使用步骤 ...