工作流程(理解)
- 启动NameNode,NameNode加载fsimage到内存,对内存数据执行edits log日志中的事务操作。
- 文件系统元数据内存镜像加载完毕,进行fsimage和edits log日志的合并,并创建新的fsimage文件和一个空的edits log日志文件。
- NameNode等待DataNode上传block列表信息,直到副本数满足最小副本条件。
- 当满足了最小副本条件,再过30秒,NameNode就会退出安全模式。最小副本条件指整个文件系统中有99.9%的block达到了最小副本数(默认值是1,可设置)
在NameNode安全模式(safemode)
- 对文件系统元数据进行只读操作
- 当文件的所有block信息具备的情况下,对文件进行只读操作
- 不允许进行文件修改(写,删除或重命名文件)
注意事项
- NameNode不会持久化block位置信息;DataNode保有各自存储的block列表信息。正常操作时,NameNode在内存中有一个block位置的映射信息。
- NameNode在安全模式,NameNode需要给DataNode时间来上传block列表信息到NameNode。如果NameNode不等待DataNode上传这些信息的话,则会在DataNode之间进行block的复制,而这在大多数情况下都是非必须的(因为只需要等待DataNode上传就行了),还会造成资源浪费。
- 在安全模式NameNode不会要求DataNode复制或删除block。
- 新格式化的HDFS不进入安全模式,因为DataNode压根就没有block。
配置信息
属性名称 | 类型 | 默认值 | 描述 |
dfs.namenode.replication.min hdfs-site.xml | Int | 1 | 写文件成功的最小副本数 |
dfs.namenode.safemode.threshold-pct | float | 0.999 | 系统中block达到了最小副本数的比例,之后NameNode会退出安全模式。小于等于0表示不进入安全模式,大于1表示永不退出安全模式 |
dfs.namenode.safemode.extension | int ms | 30000 | 当副本数达到最小副本条件之后安全模式延续的时间。对于小的集群(几十个节点),可以设置为0 |
命令操作(了解)
通过命令查看namenode是否处于安全模式:
$ hdfs dfsadmin -safemode get
Safe mode is ON
HDFS的前端webUI页面也可以查看NameNode是否处于安全模式。
有时候我们希望等待安全模式退出,之后进行文件的读写操作,尤其是在脚本中,此时:
$ hdfs dfsadmin -safemode wait
# your read or write command goes here
管理员有权在任何时间让namenode进入或退出安全模式。进入安全模式:
$ hdfs dfsadmin -safemode enter
Safe mode is ON
这样做可以让namenode一直处于安全模式,也可以设置dfs.namenode.safemode.threshold-pct为1做到这一点。
离开安全模式:
$ hdfs dfsadmin -safemode leave
Safe mode is OFF
HDFS写文件流程(重点)
流程
- 调用客户端的对象DistributedFileSystem的create方法;
- DistributedFileSystem会发起对namenode的一个RPC连接,请求创建一个文件,不包含关于block块的请求。namenode会执行各种各样的检查,确保要创建的文件不存在,并且客户端有创建文件的权限。如果检查通过,namenode会创建一个文件(在edits中,同时更新内存状态),否则创建失败,客户端抛异常IOException。
- DistributedFileSystem返回一个FSDataOutputStream对象给客户端用于写数据。FSDataOutputStream封装了一个DFSOutputStream对象负责客户端跟datanode以及namenode的通信。
- FSDataOutputStream对象将数据切分为小的数据包(64kb,core-default.xml:file.client-write-packet-size默认值65536),并写入到一个内部队列(“数据队列”)。DataStreamer会读取其中内容,并请求namenode返回一个datanode列表来存储当前block副本。列表中的datanode会形成管线,DataStreamer将数据包发送给管线中的第一个datanode,第一个datanode将接收到的数据发送给第二个datanode,第二个发送给第三个。。。
- DFSOoutputStream维护着一个数据包的队列,这的数据包是需要写入到datanode中的,该队列称为确认队列。当一个数据包在管线中所有datanode中写入完成,就从ack队列中移除该数据包。如果在数据写入期间datanode发生故障,则执行以下操作
- 关闭管线,把确认队列中的所有包都添加回数据队列的最前端,以保证故障节点下游的datanode不会漏掉任何一个数据包。
- 为存储在另一正常datanode的当前数据块指定一个新的标志,并将该标志传送给namenode,以便故障datanode在恢复后可以删除存储的部分数据块。
- 从管线中删除故障数据节点并且把余下的数据块写入管线中另外两个正常的datanode。namenode在检测到副本数量不足时,会在另一个节点上创建新的副本。
- 后续的数据块继续正常接受处理。
- 在一个块被写入期间可能会有多个datanode同时发生故障,但非常少见。只要设置了dfs.replication.min的副本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标副本数(dfs.replication默认值为3)。
- 如果有多个block,则会反复从步骤4开始执行。
- 当客户端完成了数据的传输,调用数据流的close方法。该方法将数据队列中的剩余数据包写到datanode的管线并等待管线的确认
- 客户端收到管线中所有正常datanode的确认消息后,通知namenode文件写完了。
- namenode已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。
了解
64KB数据包包含的头部信息
packet接收者:
64KB数据包格式:
HDFS读文件流程(重点)
流程
- 客户端通过FileSystem对象的open方法打开希望读取的文件,DistributedFileSystem对象通过RPC调用namenode,以确保文件起始位置。对于每个block,namenode返回存有该副本的datanode地址。这些datanode根据它们与客户端的距离来排序。如果客户端本身就是一个datanode,并保存有相应block一个副本,会从本地读取这个block数据。
- DistributedFileSystem返回一个FSDataInputStream对象给客户端读取数据。该类封装了DFSInputStream对象,该对象管理着datanode和namenode的I/O,用于给客户端使用。客户端对这个输入调用read方法,存储着文件起始几个block的datanode地址的DFSInputStream连接距离最近的datanode。通过对数据流反复调用read方法,可以将数据从datnaode传输到客户端。到达block的末端时,DFSInputSream关闭与该datanode的连接,然后寻找下一个block的最佳datanode。客户端只需要读取连续的流,并且对于客户端都是透明的。
- 客户端从流中读取数据时,block是按照打开DFSInputStream与datanode新建连接的顺序读取的。它也会根据需要询问namenode来检索下一批数据块的datanode的位置。一旦客户端完成读取,就close掉FSDataInputStream的输入流。
- 在读取数据的时候如果DFSInputStream在与datanode通信时遇到错误,会尝试从这个块的一个最近邻datanode读取数据。它也记住那个故障datanode,保证以后不会反复读取该节点上后续的block。DFSInputStream也会通过校验和确认从datanode发来的数据是否完整。如果发现有损坏的块,就在DFSInputStream试图从其他datanode读取其副本之前通知namenode。
注意
namenode告知客户端每个block中最佳的datanode,并让客户端直接连到datanode检索数据。由于数据流分散在集群中的所有datanode,这样可以使HDFS可扩展到大量的并发客户端。同时,namenode只需要响应block位置的请求,无需响应数据请求,否则namenode会成为瓶颈。