HDFS的表征是怎么着,客户端就能从流中读取数据了

引子

何以要求HDFS?

因为一个大体计算机的囤积已经hold不住大家大幅度的多少集。

HDFS的性状是什么?

HDFS以流式数据访问情势来存储超大文件,运行于商用硬件集群上

1.重特大文件:数量级MB、GB、TB等

2.流式数据访问格局:以块为单位展开读写。一遍写入、数十次读取。

3.高数量吞吐量,时间推迟不低

4.不可以储存多量小文件:namenode的内存中蕴藏HDFS中文件元音信,每个元音讯大致占150B,由此HDFS能储存的文本总数受限于namenode的内存大小。

5.不接济多用户写入:HDFS中的文件只有一个writer

6.不可以随随便便修改文件:写操作是充实形式


HDFS读写流程剖析

正文为 《Hadoop The Definitive Guide 4th
Edition》的读书笔记(或者叫翻译),仅限调换使用, 转发请注脚出处。

基础概念

分析读流程

下边这几个图片 3-2 计算性的讲述了读文件时客户端与 HDFS 中的 namenode,
datanode 之间的数量流动。

亿万先生官方网站: 1

从HDFS中读取数据

客户端首先通过在 FileSystem 上调用 open() 方法打开它想要打开的文件, 对于
HDFS 来说, 就是在 DistributedFileSystem 的实例上调用(第1步)。 之后
DistributedFileSystem 就使用 remote procedure call(RPCs)去呼叫
namenode,去考察组成文件的前多少个块的岗位(第2步)。对于每一个块,namenode
重回拥有块拷贝的 datanode 的地方。幸运的是,那些 datanode
会根据与客户端的类似度来排序(接近度是安份守己集群互连网中的拓扑结构来总括的,后边会说到)。即使客户端节点自己就是一个
datanode,而且该节点的肚子里存了一个块的正片,客户端就直接从地方datanode 读取块。

DistributedFileSystem 重临一个 FSDataInputStream(帮助文件 seek
的输入流)给客户端,客户端就能从流中读取数据了。 FSDataInputStream
中封装了一个管制了 datanode 与 namenode I/O 的 DFSInputStream。

接下来客户端就调用 read() 方法(第3步)。 存储了文件的前多少个块的地方的
DFSInputStream,就会接二连三存储了第三个块的率先个(目前的) datanode。 然后
DFSInputStream 就经过重复调用 read() 方法,数据就从 datanode
流动到了客户端(第4步)。当 该 datanode 中最终一个块的读取已毕了,
DFSInputStream 会关闭与 datanode
的连接,然后为下一块寻找最佳节点(第5步)。那几个进度对客户端的话是晶莹剔透的,在客户端那边看来,似乎只读取了一个三番五次不停的流。

块是按顺序读的,通过 DFSInputStream 在 datanode
上开辟新的总是去作为客户端读取的流。他也将会呼叫 namenode
来得到下一批所要求的块所在的 datanode 的职位(注意刚才说的只是从 namenode
获取前多少个块的)。当客户端达成了读取,就在 FSDataInputStream 上调用
close() 方法截至所有流程。

在读取进度中, 即使 FSDataInputStream 在和一个 datanode
进行互换时现身了一个错误,他就去试一试下一个最接近的块,他自然也会铭记刚才暴发错误的
datanode 以至于之后不会再在那个 datanode 上开展没须要的尝尝。
DFSInputStream 也会在 datanode
上传输出的多少上查处检查数(checknums).要是破坏的块被发现了,DFSInputStream
就打算从另一个颇具备份的 datanode 中去读取备份块中的数据。

在那些安顿中一个爱护的方面就是客户端直接从 datanode 上追寻数据,并透过
namenode 辅导来博取每一个块的极品 datanode。那种安插允许 HDFS
扩张大量的并发客户端,因为数量传输只是集群上的富有 datanode
展开的。时期,namenode
仅仅只须要服务于获取块地方的伸手(块地方音讯是存放在在内存中,所以作用很高)。若是不那样设计,随着客户端数据量的增进,数据服务就会神速成为一个瓶颈。

集群上的拓扑结构

咱俩知道,相对于客户端(之后就是 mapreduce task
了),块的地方有以下可能:

  • 在客户端所在节点上(0,也就是本地化的)
  • 和客户端不在同一个节点上,但在同一个机架上(2)。
  • 和客户端不在同一个机架上,可是在同一个数码基本里(4)。
  • 不怕与客户端不在一个多少要旨(6)。

HDFS ( datanode? namenode?那里自己也不驾驭是什么人来完毕这么些排序)
就是按照上面的多种可能性来对节点进行类似度计算。他们的分值分别为
0,2,4,6:

亿万先生官方网站: 2

图片 3-3

数据块

作为独立的存储单元,读写最小单位。默许64MB,可在hdfs-site.xml中自定义。

块要比磁盘块(512B)大得多,是因为最小化寻址开支。磁盘传输数据耗时>定位那些块初始地方的耗时。不过块不可能安装过大,是因为MR任务中,map任务日常一回拍卖一个块,假诺块数量少,则并行map职务就少,job运行速度较慢。

再说说……

· 文件所有的块分布式存储在挨家挨户datanode上,

· 小于一个块默认大小的公文,不会占据整个块的半空中。

剖析写流程

写流程的图如下:

亿万先生官方网站: 3

image

率先客户端通过在 DistributedFileSystem 上调用 create()
方法(第1步)来创造一个文件。 DistributedFileSystem 使用 RPC 呼叫 namenode
,让她
在文件系统的命名空间上成立一个尚未与任何块提到的新文件(第2步), namenode
会执行种种各种的检讨以确认文件此前是不设有的,并确认客户端是还是不是有所成立文件的权能。要是检查通过。
namenode
就会为新文件生成一条记下;否则,文件创设就会败北,客户端会抛出一个
IOException。 成功之后,DistributedFileSystem 会重回一个
FSDataOutputStream
给客户端以让她早先写多少。和读流程中一致,FSDataOutputStream 包装了一个
DFSOutputStream,他牵线了与 datanode 与 namenode 的联络。

当客户端起来写多少(第3步),DFSOutputStream
将文件分割成很多很小的多少,然后将每个小块放进一个个包(数据包,包中除了数量还有描述数据用的标识)中,
包们会写进一个名为数据队列(data quence)的其中队列。数据队列被
DataStreamr 消费,他承受须求 namenode 去挑选出适合储存块备份的 datanode
的一个列表(注意,那里是文件的一个块,而不是任何文件)。那些列表会构成一个
pipeline(管线),那里假定备份数为3,所以在 pipeline 中就会有七个 datanode
, DataStreamer 将可以结合块的的包先流入 pipeline 中的首个 datanode
,第四个 datanode 会先存储来到的包,然后继续将所有的包转交到 pipeline
中的第一个 datanode 中。相似的,首个 datande
也会蕴藏那么些包,并将他们传递给 pipeline 中的第七个(最终一个) datanode
(第4步)。

多少的流动的法子应该还有二种,第一种就是首个 datanode
获得所有的数目包后并写入后,才将数据包往下传递;第三种就是一旦数据包写入成功就平昔传给下一个
datanode,这种可能性最大。不影响全局,具体是哪一种待确认。注意那里的写入就是写入到磁盘里。

DFSOutputStream
也会爱戴一个包们的里边队列,其中也会有所有的数据包,该队列等待
datanode们 的写入确认,所以称为确认队列(ack quence)。当一个包已经被
pipeline 中的所有 datanode 确认了写如磁盘成功,那些包才会从
确认队列中移除(第5步)。固然在此外一个 datanode
在写入数据的时候败北了,接下去所做的整个对客户端都是晶莹剔透的:首先,
pipeline
被关门,在肯定队列中的剩下的包会被添加进数据队列的苗子地点上,以至于在挫折的节点下游的任
何节点都不会丢掉任何的包。

那边有些难点,就是数码包写数据时的多寡队列的景况,是一贯不变,写入了再移除,照旧已经清空了。按照下边的说法,失利了就将余下的还未写入的数目包添加(应该是拷贝)回数据队列,数据队列“一向不变”和“写入了再移除数据包”不就会并发重复了。而清空的话,应该是失误了后来才清空。那那样怎么不用数据队列作为确认队列,当发现都写入成功了,就将包从队首移除?
这些也待确认。

接下来与 namenode 联系后,当前在一个好的 datanode 会联系 namenode,
给败北节点上还未写完的块生成一个新的标识ID, 以至于即使那么些失利的
datanode 不久后重操旧业了,这几个不完整的块将会被删去。

亿万先生官方网站:,失利节点会从 pipeline 中移除,然后剩下五个好的 datanode 会组成一个的新的
pipeline ,剩下的 这几个块的包(也就是刚刚放在数据队列队首的包)会再三再四写进
pipeline 中好的 datanode 中。

末段,namenode
注意到块备份数小于规定的备份数,他就布局在另一个节点上创办已毕备份,间接从已部分块中复制就能够。然后径直到满足了备份数(dfs.replication)。

设若有七个节点的写入战败了,假使满足了小小的备份数的安装(dfs.namenode.repliction.min),写入也将会中标,然后剩下的备份会被集群异步的实施备份,直到知足了备份数(dfs.replication)。

当客户端达成了数码写入,会在流上调用 close() 方法(第6步)。
那么些作为会将有着盈余的包刷新(flush)进 datanode
中,然后等待确认音信达到后,客户端就联系 namenode
告诉她文件数量现已放好了(第七步)。namenode
也一向清楚文书被分为了何等块(因为在前边是 DataStreamer
请求了块分配),所以现在在中标此前,只须求拭目以待块满意最低限度的备份(因为刚刚提到的败诉)。

namenode和datanode

namenode管理文件系统的命名空间和种种文件中逐条块所在的数据节点音讯。命名空间是HDFS的文件系统树以及树内所有目录和文书,以fsimage和editlog文件永远保存在地点磁盘上。块的存储音讯在内存中,系统启动时由datanode上报。

datanode是HDFS的办事节点,负责储存并寻找数据块,定期向namenode发送它们所蕴藏的块的列表。

关于配置:

dfs.replication默许3,一个数目块存3份,HDFS会自动备份到3个不等的datanode上。


End!!

HDFS读写流程

读文件

【一句话版本】namenode告知客户端数据的块地点,让客户端联系datanode流式检索数据。

利益:
namenode内存存储块索引消息,相应快;block分散在集群拥有节点上,以便HDFS可伸张大批量并发客户端。

瓶颈:随客户端数量拉长,namenode的I\O成为瓶颈。

1.
【概括版】客户端调用DistributedFileSystem对象的open()方法,RPC调用namenode的GetBlockLocations()方法,namenode重返存有该公文所有block新闻,包涵其副本所在的所有datanode地址

【细节版】客户端调用DistributedFileSystem.open(Path f, int
bufferSize),open()函数中new了一个DFSInputStream对象;在DFSInputStream的构造函数中,openInfo()函数被调用,其首要性从namenode中赢得要打开的文件所对应的blocks的音信,通过callGetBlockLocations()达成,主题代码如下:

// openInfo():

LocatedBlocks newInfo = callGetBlockLocations(namenode, src, 0,
prefetchSize);

//callGetBlockLocations()将官发起一个RPC调用,重临 LocatedBlocks
对象

namenode.getBlockLocations(src, start, length);

LocatedBlocks 是一个链表,List<LocatedBlock>
blocks,其中每个元素包括以下新闻:

Block b:此block的信息

long offset:此block在文件中的偏移量

DatanodeInfo[] locs:此block位于哪些DataNode上

2.
namenode吸纳到请求后,将开展一连串操作。RPC调用NameNode.getBlockLocations(),里面再调用namesystem.getBlockLocations(getClientMachine(),
src, offset, length);

namesystem保存着namenode上的命名空间树,具体是一个INode链表,INode有三种子类:INodeFile和INodeDirectory。其中,INodeFile有成员变量BlockInfo
blocks[],是此文件包涵的block的信息。

getBlockLocations()具体步骤:1) 得到此文件的block信息; 2)
从offset开端,长度为length所提到的blocks; 3)
找到各类block对应的、健康的datanode机器。再次来到LocatedBlocks对象。

3~5.
回到客户端,在DFSInputStream的构造函数通过RPC收到一串block音信(即LocatedBlocks对象)之后,DFSInputStream读取文件起先块的datanode地址,随即与离开方今的datanode建立Socket连接和读入流,客户端反复调用FSDataInputStream的read()读取block音信

e.g.对于64M一个block的文件系统来说,欲读取从100M(offset)开首,长度为128M(length)的多少,则block列表包涵第2,3,4块block。第2号block从36MB开端读取28MB,第3号block从0MB伊始读取64MB….

抵达block末端,DFSInputStream关闭与该datanode的一而再,寻找下一个block的最佳datanode。

6.到达文件末端时,客户端对FSDataInputStream调用close()方法。

再说说…

赶上读失利,1)
DFSInputStream和datanode的连天发生错误时,从距离次近的datanode读取,并将该节点记入“故障节点列表”,避防反复从该节点读。2)读取到一个破坏的block,先布告namenode,再从任何datanode读取该块的另一个副本。

写文件

【一句话版本】客户端向namenode申请成立文件,namenode分配datanode,客户端通过pipeline方式写多少,全体认同正常写入后通报namenode。

1.客户端通过调用DistributedFileSystem对象的create()方法,该方法生成一个FSDataOutputStream用于向新转变的文书中写入数据,其成员变量dfs的项目为DFSClient,DFSClient的create()函数中回到一个DFSOutputStream对象。在DFSOutputStream的构造函数中,做了两件紧要的作业:一是经过RPC调用NameNode的create()来创制一个文本;二是streamer.start(),即起步了一个pipeline,用于写多少。

//以下为客户端调用的create                                           
                                      public FSDataOutputStream
create(Path f, FsPermission permission,
boolean overwrite, int
bufferSize, short replication, long blockSize,
Progressable
progress) throws IOException {
return new
FSDataOutputStream(dfs.create(getPathName(f), permission,

overwrite, replication, blockSize, progress, bufferSize),
statistics);  }

  1. namenode
    先在命名空间(在磁盘)中检查文件是不是留存以及客户端是否有权力,再新建一个新文件的元音信到fsimage
    中,就是命名空间,此时从不其余块与之对应。

3~5.
客户端调用DFSOutputStream对象的write()方法写多少。根据HDFS的设计,对block的多少写入使用的是pipeline的方法,也就要数据分为一个个的package,若是急需复制三分,分别写入DataNode
1, 2, 3,则会举办如下的经过:

    1) 创造写入流,RPC调用namenode为文件分配block,
并设置block对应的DataNode。

    2) 将block分成若干个chunk(512B),每N个chunk +
校验值形成一个package。package进入dataQueue

    3) 客户端将DataNode 2、3信息和 package 1写入DataNode 1,package
1从dataQueue移至ackQueue,等待确认。

    4) 由DataNode 1负责将DataNode3信息和package1写入DataNode
2,同时客户端可以将pacage 2写入DataNode 1。package
2从dataQueue移至ackQueue,等待确认。

    5) DataNode 2负责将package 1写入DataNode 3, 同时客户端能够将package
3写入DataNode 1,DataNode 1将package 2写入DataNode 2。package
3从dataQueue移至ackQueue,等待确认。

就这么将一个个package排着队的传递下去,直到所有的多少总体写入并复制完成并且都接受到ACK确认包。

6~7.写完所有块之后,断开与DataNode 1的连接,客户端文告namenode,落成。

再说说….

赶上写失利,DataNode1故障时,1)关闭pipeline,2)把ackQueue中的所有数据包添加到dataQueue的头顶,
3)为DataNode2中当前block指定一个新标识,公告namenode,以便DataNode1恢复生机后去除本block的不尽数据,4)将故障DataNode1从pipeline中删除,然后继续将余下数量包写入正常节点。异步完花费block的副本复制。

关于“文件一致性”:在文件创设后,写完前,正在写入的block是读取不到的(e.g.读文件内容、获取文件大小)。除非调用HDFS的sync()方法强制所有缓存与数量节点同步。

参照小说:

《Hadoop- The Definitive Guide, 4th Edition》

Hadoop学习总括之二:HDFS读写进程解析

网站地图xml地图