写入
请求读取,允许读取,请求第一个块地址,返回queue,从queue拿一个地址读取,请求下一个块并循环,读取结束,请求关流
1. 客户端发起RPC请求到NameNode
2. NameNode收到请求之后,先校验:
- 校验是否有权限操作要写入的路径
- 校验这个路径下是否有同名文件
- 如果校验通过,那么NameNode给客户端返回一个信号表示允许写入
- 客户端收到信号之后,就向NameNode发送请求,请求获取第一个Block的写入位置
- NameNode收到请求之后,将位置放到一个队列中返回给客户端
- 客户端收到队列之后,从队列中将全部的位置取出,从中选择一个较近的节点,写入这个Block的第一个副本,同时客户端告诉这个节点第二个副本和第三个的副本的存储位置。当前节点通过pipeline(管道,本质上就是一个NIO中的Channel【fileChannel加上SocketChannel的结合】)将第二个副本写到对应节点上;第二个副本所在的节点再通过管道将数据写到第三个副本上。等第三个副本写完之后,会给第二个副本所在的节点返回一个ack表示写入成功;第二份副本所在的节点收到ack之后会给第一个副本所在的节点返回一个ack表示成功;第一个副本所在的节点收到ack之后,会给客户端返回一个ack表示写入成功 【集群内部备份的速度是比从集群外上传速度要快的】
- 客户端收到ack,就会向NameNode要下一个Block的位置,重复5.6.7三个步骤
- 当客户端将所有的Block写完之后,通知NameNode关闭文件(关流)
读取
1. 客户端发起RPC请求到NameNode
2. NameNode收到请求之后,查询元数据,如果查询成功,会给客户端返回信号表示允许读取
3. 客户端收到信号之后,就会向NameNode发送请求,请求获取第一个Block的存储位置 (一个一个的给,请求一个nn返回一个的地址)
4. NameNode在收到请求之后,会将这个Block的存储位置(默认情况下,副本数量为3,所以放到队列中的位置也是3个)放到一个队列中返回给客户端
5. 客户端收到队列之后,会将这个Block的位置从队列中全部取出来,从这些位置中选取一个比较近(以网络拓扑距离来算)的DataNode来进行读取。在读取的时候,会读取Block以及对应的.meta(.meta对当前Block的描述,例如这个Block的产生时间、Block的大小等。meta就是用来读取完成之后校验block的) ,这个能够保证每个block不会出错
6. 当客户端读取完成之后,会对Block的大小进行校验。如果校验失败,那么客户端就会通知NameNode,同时客户端重新选取地址重新读取;如果读取成功,那么客户端会继续向NameNode发送请求要下一个Block的地址,重复4.5.6三个步骤
7. 当客户端读取完所有的Block之后,客户端就会给NameNode发送信号要求关闭文件(关流)
删除
1. 客户端发起RPC请求到NameNode
2. NameNode收到请求之后,会进行校验:
a. 校验是否有文件
b. 校验是否有删除权限
3. 如果校验通过,NameNode就会将这个写操作记录到edits_inprogress中,同时更改内存中的元数据。元数据更改完成之后,NameNode就会给客户端返回一个ack表示删除成功。这个过程中,仅仅是元数据做了修改,这个文件对应的Block并没有被删除,也就意味着,客户端收到ack不代表文件的已经删除
4. 当NameNode收到DataNode 心跳的时候,会校验这个心跳信息。如果发现心跳信息和元数据不一致,那么NameNode就会进行心跳相应要求DataNode删除对应的Block
5. 当DataNode收到心跳相应之后,才会删除Block。此时这个文件才算真正从HDFS上移除