1.HDFS 写操作
1.1图解HDFS读过程
1.2 数据写入过程详解
1、使用HDFS提供的客户端Client,向远程的NameNode发起RPC请求;
2、NameNode会检查要创建的文件是否已经存在,创建者是否有权限进行操作,成功则会为文件创建一个记录,否则会让客户端抛出异常;
3、当客户端开始写入文件的时候,客户端会将文件切分为多个packets,并在内部以数据队列“data queue(数据队列)”的形式管理这些packets,并向NameNode申请blocks,获取用来存储replicas的合适datanode列表,列表的大小根据NameNode中的replication的设置而定;
4、开始以pipeline(管道)的形式将packet写入到所有的replicas中。客户端把packet以流的方式写入到第一个DataNode,该DataNode把该packet存储之后,在将其传递给再次pipeline中的下一个DataNode,直到最后一个DataNode,这种写数据的方式呈流水线的形式。
5、最后一个DataNode成功存储之后会返回一个ack packet(确认队列),在pipeline里传递至客户端,在客户端的开发库内部维护者“ack queue”,成功收到DataNode返回的"ack packet"后会从“DataNode”移除相应的packet;
6、如果传输过程中,有某个DataNode出现了故障,那么当前的pipeline会被关闭,出现故障的DataNode会从当前的pipeline中移除,剩余的block会继续剩下的DataNode中继续以pipeline的形式传输,同时,NameNode会分配一个新的DataNode,保持replicas设定的数量;
7、客户端完成数据的写入后,会对数据流调用close()方法,关闭数据流;
8、只要写入了dfs.replication.min(最小写入成功的副本数)的副本数(默认为1)。写操作就会成功,并且这个快可以在集群种异步复制,直到达到其目标副本数(dfs.replication的默认值为3),因为NameNode已经知道文件由哪些块组成,所以它在返回成功之前只需要等待数据块进行最小量的复制。
1.3详细说明
1、客户端发起请求:hdfs dfs -put a.avi /movie
客户端如何知道请求发送给哪个节点的哪个进程?
客户端会提供一些工具来解析所指定的HDFS集群中的主节点是谁,以及端口号等信息,主要是通过URI来确定 URl:hdfs://master1:9000
当前请求包含了一个非常重要的信息:上传数据的总大小
2、NameNode会响应客户端的这个请求:
NameNode的职责:
1、管理元数据(抽象目录树结构)
- 用户上传的那个文件在对赢的目录中如果存在,那么HDFS集群应该作何处理? 不会处理。
- 用户上传的那个文件要存储的目录不存在? 如果不存在不进行创建
2、响应操作
真正的操作:进行一系列的校验- 校验客户端的请求是否合理
- 校验客户端是否有权限进行上传操作
3、如果NameNode返回给客户端的结果是通过,那就是允许上传
namenode会给客户端返回对应的所有数据块的多副本的存放节点列表,如:
file1_blk hadoop02,hadoop03,hadoop04
file2_blk hadoop03,hadoop04,hadoop05
4、客户端在获取到了namenode返回回来的所有数据块的多个副本存放地址的数据之后,就可以按照顺序逐一进行数据块的上传操作
5、对要上传的数据块进行逻辑切分
6、开始上传第一个数据块
7、客户端会做一系列的准备操作
- 依次发送请求去对应的DataNode
pipeline:client -node1 -node2 -node3
按照一个个的数据包的形式进行发送
每次传输完一个数据包,每个副本节点都会进行校验,依次原路返回给客户端- 在客户端会启动一个服务:
用户等待将要从pipeline数据管道上进行传输的数据包的校验信息
客户端就能知道当前从client写到node1,2,3三个节点上的数据是否都写入正确和成
8、client会正式的吧这个块中的所有packet都写到对应的副本节点
1、block是最大的一个单位,它是最终存储于DataNode上的数据粒度,由dfs.block.size参数决定,2.x版本默认是128M;注:这个参数由客户端配置决定;
如:System.out.println(conf.get(dfs.blocksize));结果是134217728
2、packet是中等的一个单位,它是数据有DFSClient流向DataNode的粒度,以dfs.write.packet.size参数为参考值,默认的是64k;注:这个参数是参考值,是指真正进行数据传输时,会以它为基准进行调整,调整的原因是一个packet有特定的结构,调整的目标是这个packet的大小刚好包括结构中的所有成员,同时也保证写到DataNode后当前的block的大小不操作设定值;
如:System.out.println(conf.get(dfs.write.packet.size));结果是65536
3、chunk是最小的一个单位,它是DFSClient到DataNode数据传输中进行数据校验的粒度,由io.bytes.per.checksum参数决定,默认是512B;注:事实上一个chunk还包含4B的校验值,因此chunk写入packet时是516B;数据与校验值的比值为128:1,所以对于一个128M的block会有一个1M的校验文件与之对应;
如:System.out.println(conf.get(io.bytes.per.cheksum));结果是512
9、Client进行校验,如果校验通过,表示该数据块写入成功
10、 重复7 8 9 三个操作,来继续上传其他的数据块
11、客户端在收到所有的数据块都写入成功后,会给NameNode发送一个反馈,告诉NameNode当前客户端上传的数据已经成功。
2.HDFS 读操作
2.1 图解HDFS读操作
2.2 数据读取过程详解
1、客户端调动FileSystem实例的open方法,获取这个文件对应的输入流InputStream。
2、通过RPC远程调用NameNode,获得NameNode中此文件对应的数据块保存位置,包括这个文件的副本的保存位置(主要是各DataNode的地址);
3、获取输入流之后,客户端调用read方法读取数据,选择最近的DataNode建立连接并读取数据;
4、如果客户端和其中一个DataNode位于同一机器(比如MapReduce 过程的mapper和reducer),那么就会直接从本地读取文件;
5、到达数据块末端,关闭与这个DataNode的连接,然后重新查找下一个数据块;
6、不断执行2-5步操作指导数据全部读完;
7、客户端调用close,关闭输入流DFSInputStream。