HDFS 基础理论
四大机制: 心跳机制, 安全模式, 机架感知, 负载均衡;
两大核心: 读写原理
1.心跳机制
master 和 slave 之间通过 ipc 服务通信, 通信有固定时间周期(默认3秒),称之为心跳。
dfs.heartbeat.interval: 配置心跳时间
slave 超时时间的计算:
timeout = 2 * dfs.namenode.heartbeat.recheck.interval + 10 * dfs.heartbeat.interval
默认值: dfs.namenode.heartbeat.recheck.interval 300000(5 min)
dfs.heartbeat.interval 3 (3 sec)
查看资料发现, datanode 的 offerService 方法中, 有着每隔3秒向 namenode 发送心跳的代码,
该代码表示, datanode 实际是对 namenode 进行调用:
public void offerService() throws Exception {
...
while (shouldRun) {
try {
long startTime = now();
// 心跳机制间隔时间判断
if (startTime - lastHeartbeat > heartBeatInterval) {
lastHeartbeat = startTime;
// 在 datanode 中执行 namenode.sendHeartbeat(...)
// 实际上 **心跳机制就是在 datanode 中 调用 namenode 的方法** !!!
DatanodeCommand[] cmds = namenode.sendHeartbeat(dnRegistration,
data.getCapacity(),
data.getDfsUsed(),
data.getRemaining(),
xmitsInProgress.get(),
getXceiverCount());
...
但是 datanode 与 namenode 是不同节点上的两个 jvm 进程, 他们具体是怎样调用的呢 ?
datanode向namenode发送heartbeat过程是这样的:
实际上是通过动态代理的方式进行调用, 以下是大佬总结步骤:
详细请移步, 一步步推理逻辑清晰 :
https://blog.csdn.net/fly542/article/details/6797139
- 在 datanode 初始化获得 namenode 的 proxy
- 在 datanode上,调用 namenode proxy 的 heartbeat 方法: namenode.sendHeartbeat ( dnRegistration,
data.getCapacity(),
data.getDfsUsed(),
data.getRemaining(),
xmitsInProgress.get(),
getXceiverCount() );- 在 datanode 上的 namenode 动态代理类将这个调用包装成 (或者叫“序列化成”) 一个Invocation对象,并调用client.call方法
- client.call 方法将 Invocation 转化为 Call 对象
- client 将 call 发送到真正的 namenode 服务器
- namenode 接收后,转化成 namenode 端的 Call,并处理后,通过 Responder 发回来!
- datanode 接收结果,并将结果转化为 DatanodeCommand[]
2.安全模式
namenode在系统启动时会首先进入安全模式, 此时对datanode进行检查数据块的有效性.
如果 datanode 丢失的数据达到 0.1%, 则一直处于安全模式, 此时只读模式, 不可修改与删除文件;
若block丢失小于 0.1% 则自动退出.
-
safemode是namenode的一种状态 ( active / standby / safemode )
集群中的文件不能被操作 (安全模式是一种自我保护)
-
进入safemode的状况
dfs.namenode.safemode.threshold-pct ( 默认值0.999f )
即: block块丢失率达到0.1%, 自动进入, 只读不可修改删除文件
-
退出safemode
方法一: 修复宕机的节点(推荐),自动退出
方法二: 强制退出safemode(没有解决问题,有可能再次出现数据丢失)
强制退出命令: hdfs dfsadmin -safemode leave
-
问题: 为什么集群启动时会自动进入 safemode, 然后又自动退出?
block 所在的 datanode 的信息存在于内存中,而不在磁盘中。
故冷启动时,刚开始找不到 block 所在的节点的, 认为 block 丢失, 后再内存中加载, 则恢复.
-
安全模式常见命令
hdfs dfsadmin -safemode get:获取当前的safemode状态
hdfs dfsadmin -safemode enter:进入safemode状态
hdfs dfsadmin -safemode leave:退出safemode状态
hdfs dfsadmin -safemode wait:等待
3.机架感知 - 副本存放策略
-
HDFS文件块副本的放置对于系统整体的可靠性和性能有关键性影响
配置机架感知作用 : 将数据分散冗余存储, 以保证数据的可靠性和高效性 -
机架感知 ( 又称副本存放策略 )
- 原则 ( 从三方面进行考虑 )
1.高可靠 (副本存放不在同一机架上, 若该机架崩溃, 在另一机架上存放的副本数据仍保全 )
2.负载均衡 (每个机架副本数均匀分布, 防止出现单个机架存放副本远多于其他机架 )
3.带宽 (跨越的机架个数, 例如只跨越了一个机架等) - 通常存放策略
1.第一个副本放在与 Client 相同机架的 Node 中(如果Client不在集群范围,第一个Node是随机选取不太满或者不太忙的Node)
2.第二个副本放在与第一个 Node 不同的机架中的 Node 中
3.第三个副本放在与第二个 Node 所在机架里不同的 Node 中
- 原则 ( 从三方面进行考虑 )
-
当没有配置机架信息时
hadoop 默认所有的机器都在同一个默认的机架下,名为 “/default-rack”
此时,任何一台datanode机器,不管物理上是否属于同一个机架,都会被认为是在同一个机架下
容易出现问题 : 增添机架间网络负载
同时: namenode 默认将所有的slaves机器全部默认为在/default-rack下,
因此在这种情况下写 block 时,三个datanode机器的选择完全是随机的
- 配置机架感知方法
1.配置之前:
首先要知道一点: 机架感知需要网络拓扑图支持
简单说, 网络拓扑图就是网络节点设备和通信介质构成的网络结构图 ( 没有地图怎么导航?)
2.启动机架感知:
默认情况下, 机架感知是没有启用的, 需要在 NameNode 节点中 hadoop-site.xml 中配置,
同时用户需要自定义脚本来配置网络拓扑结构:
3.自定义脚本<property> <name>topology.script.file.name</name> <value>/path/to/script</value> 脚本名称及路径 </property>
脚本接受一个参数,输出一个值。
接受的参数通常为 datanode 机器的 ip 地址 (此参数由心跳机制通信传输信息得到 )
输出的值通常为该 ip 地址对应的 datanode 所在的机架编号( rackID )
过程:
Namenode 启动时,会判断该配置选项是否为空,if not null,则表示已经启用机架感知的配置.
此时 namenode 会根据配置寻找该脚本:
接收到每一个datanode的heartbeat时,将该 datanode 的 ip地址 作为参数传给该脚本运行,
将得到的输出作为该 datanode 所属的机架,保存到内存的一个 map 中。
脚本的编写
需将真实网络拓朴和机架信息了解清楚后,通过脚本能将机器的ip地址正确映射到相应的机架上
官方参考: http://wiki.apache.org/hadoop/topology_rack_awareness_scripts 截图如下
在脚本编写完成后, 查看网络拓扑结构命令: hdfs dfsadmin -printTopology
至此机架感知配置完成, hadoop在选择三个 datanode 时,就会按照副本存放策略进行相应的判断.
此时这三步就进行 block 的写入:
- 得到 3个 datanode 的列表以后,从返回到客户端之前,会在 namenode 端首先根据该客户端跟每个 datanode 之间的“距离”由近到远进行一个排序,客户端根据这个顺序有近到远的进行数据块的写入
- 当根据“距离”排好序的 datanode 节点列表返回给 Client 以后,Client便会创建 Block OutputStream,并向这次 block 写入 pipeline 中的第一个节点(最近的节点)开始写入block数据
- 写完第一个block以后,依次按照datanode列表中的次远的node进行写入,直到最后一个block写入成功,Client 返回成功,该 block 写入操作结束
namenode在选择数据块的写入datanode列表时,就充分考虑到了将block副本分散在不同机架下,并同时依据从近到远避免了多余的网络开销.
需要注意的是:
- HDFS中存储的文件的副本数由上传文件时设置的副本数决定
无论以后怎么更改系统副本系数,这个文件的副本数都不会改变 - 上传文件时优先使用启动命令中指定的副本数, 若启动命令中未指定则使用hdfs-site.xml中配置
修改hdfs-site.xml
dfs.replication
通过shell命令实现:
hdfs dfs -setrep 2 文件
4.负载均衡
- 产生原因
HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,
例如:当集群内新增、删除节点,节点宕机或者某个节点机器内硬盘存储达到饱和值。 - 出现问题
数据不平衡, Map任务可能会分配到没有存储数据的机器
导致网络带宽的消耗,也无法很好的进行本地计算, 此时需要对 HDFS 进行数据负载均衡调整 - 负载均衡
负载均衡需要遵循一定原则
1.数据平衡不能导致数据块减少,数据块备份丢失
2.管理员可以中止数据平衡进程
3.每次移动的数据量以及占用的网络资源,必须是可控的
4.数据均衡过程,不能影响 namenode 的正常工作
命令:
实现负载均衡,默认移动速度1m/s(出于带宽考虑,读写)
start-balancer.sh:
设置默认的移动速度
hdfs dfsadmin -setBalanacerBandwidth 10485760:
负载最高的节点和最低节点之间的数据差距比例不超过10%
stat-balancer.sh -t 10%:
- 数据均衡原理
请移步大佬博客: https://www.cnblogs.com/BYRans/p/5128162.html
5.读原理
读流程总结
- client 向 namenode 发送文件下载 (RPC) 请求
client 调用 FileSystem 对象中 open( ) 方法, 获取文件输入流 - namenode 在元数据中进行校验请求下载文件是否存在
① 不存在, 报错
② 存在, 返回对应文件的 blkid 以及每一个 blkid 对应的的每一个副本的存放位置
③ 返回格式: /hadoop.tar.gz [blk001:[hdp01 hdp02] blk002:[hdp01 hdp03]] - client 开始下载第一个数据块, 就近原则
client 使用 InputStream 中 read( ) 与最近的 datanode 建立连接读取数据 - 第一个数据块下载完成, 进行 crc 文件校验, 检验通过, 下载成功
- 重复下载步骤, 将内容自动追加到之前的数据块末尾
- 直至所有数据块下载成功, 返回给客户端.
至此 HDFS 读流程 ( 文件下载 ) 全部结束, 开始重点部分:
6.写原理
写流程总结
- client 向 namenode 发送上传文件 (RPC) 请求
client 提供工具根据 url 解析 Hadoop 主节点及端口信息, 来确定将请求发送给哪个节点哪个进程 - namenode 在元数据中进行检查, 上传文件目录是否存在, 客户端是否具有权限, 请求是否合理等
上传文件目录必须存在, 若不存在, 则需要手动创建, 并不会自动创建 - namenode 检查通过, 返回 client 允许上传
- client 提交真正上传文件的请求, 包含一个重要信息, 上传文件的大小
- namenode 返回所有数据块的多个副本的存放节点列表
在此过程中, namenode 完成三个操作
① 通过文件大小计算切块个数 ( 文件总长 / 128M = 切块个数, 向上取整)
② 从配置文件中获取副本个数
③ 返回节点列表, 按照就近原则返回 ( 同节点 → 同机架 → 同机房), 格式如下:
file1_blk1 hadoop02,hadoop03,hadoop04
file1_blk2 hadoop03,hadoop04,hadoop05 - client 准备上传文件, 预先进行逻辑切块, 为物理切块做准备
逻辑切块 : 范围划分, 将数据的每一个数据块对应的数据偏移量范围划分出来, 没有实际切分
物理切块 : 将需要上传的数据 按照逻辑切块的区域划分进行真正的切块 - 开始真正的文件上传, 准备上传第一个数据块
- 准备工作:
① client 构建第一个数据块上传的通道 pipline
构建过程: 依次从 client → 第一个副本节点 → 第二个副本节点 → … 并依次向前反馈构建结果
② 同时 client 开启一个守护进程 (阻塞) , 等待上传结果的反馈 - pipline 构建完成, 开始上传第一个数据块
文件上传:
① client 边进行物理切分边进行上传
② 以 packet 为单位上传, 以 dfs.write.packet.size 参数为参考, 默认是 64 K,
同样, 上传过程: 依次从 client → 第一个副本节点 → 第二个副本节点 → …
③ 每次上传一个数据块,每个副本节点都会进行校验,校验结果依次原路给客户端 - client 守护线程等待上传文件校验结果通过, 表示写入成功
- 重复上传文件步骤, 直至所有数据都上传成功, 此时 client 向 namenode 发送上传成功反馈
namenode 接到文件上传成功反馈, 修改 namenode 中的元数据
至此 HDFS 写流程 ( 文件上传 ) 全部结束.