HDFS原理学习笔记
一、HDFS概述
HDFS的是一个Master/Slave分布式系统(NameNode是主节点,DataNode是从节点)
1.数据块(Block):
数据块的默认大小是128MB,HDFS将数据块的冗余备份(默认冗余3份)保存到不同的数据节点上,
2.NameNode
NameNode负责管理文件系统的命名空间(包括文件系统目录树、文件/目录信息和文件的数据库索引,这些信息以文件的形式永久保存在NameNode的本地磁盘)以及数据块到具体DataNode节点的映射等信息(这部分数据不保存在NameNode本地磁盘上,而是在NameNode启动时动态构建的)。
3.DataNode
DataNode会不断地向NameNode发送心跳,数据块汇报和缓存汇报,NameNode可以向DataNode发送指令,如创建、删除或复制数据。
二、Hadoop RPC
RPC(Remote Procedure CallProtocol)允许本地程序像调用本地方法一样调用远程机器上应用程序的应用服务,Hadoop底层通过JavaNIO、Java动态代理和Protobuf等实现。
2.1 Hadoop RPC的实现步骤:
1.定义RPC协议:
即定义客户端和服务器之间的RPC调用的接口,这样客户端就知道Server提供了那些服务;
2.实现RPC协议:
即服务端程序需要实现RPC协议,对RPC请求能够做响应的处理;
3.客户端获取代理对象:
客户端使用RPC.getProtocolProxy()获取一个RPC协议的代理对象,然后通过代理对象调用RPC协议的方法(通过JAVA动态代理机制);
4.服务器构造并启动RPC Server:
服务器通过调用RPC.Builder.build()方法构造Server对象,然后start来启用server对象来相应客户端的RPC请求。
2.2 客户端获取Proxy对象
DFSClient通过ProxyInfo类来调用NameNodeProxies.createProxy()方法创建代理(默认使用ProtobufRpcEngine序列化引擎实现),根据Hadoop的配置判断当前HDFS是否是HA模式。
1.非HA模式:
客户端通过createProxy() 调用createNonHaProxy()创建普通的ClientProtocol对象;
2.HA模式(即两个NameNode实例):
DFSClient通过代理对象发送请求后,代理对象来讲请求发送到Active NameNode,如果连接失败则会重试,重试达到一定的次数后,DFSClient的代理对象又将请求发送给StandBy NameNode。
2.3 服务器获取Rpc Server对象
NameNodeRpcServer实现了ClientProtocol、NameNodeProtocol、DataNodeProtocol和HAServiceProtocol在内的所有需要与Namenode进行交互的RPC协议接口。Namenode主要构造两个RPC Server,即ClientRpcServer(负责相应HDFS的客户端的RPC请求)和ServiceRpcServer(负责相应Datanode的RPC请求)
三、Namenode
HDFS文件系统的命名空间是在Namenode的内存中以树的结构来存储的,不管是目录还是文件,都被当做INode节点。如果是目录,则对应的类为INodeDirectory(包含一个成员集合变量children);如果是文件,则对应的类为INodeFile。INodeDirectory和INodeFile是INode的派生类。
1.INode类
1.1 INode继承的方法
(继承自INodeAttribute接口的get方法userName/groupName/fsPermission/aclFeature/modificationTime/accessTime/XAttrFeature);
1.2 INode自带的属性方法(id、name、fullPathName、parent)和方法(isFile/isDirectory/isSymlink/isRoot)
2.INodeDirectory类
是HDFS的虚拟容器,主要处理器属性children的增删改查以及Feature和快照的处理方法等。
3.INodeFile类
INodeFIle类保存了文件头header字段(保存了当前文件的副本数和文件数据块的大小)和文件对应的数据库块信息blocks字段(一个BlockInfo类型的数据,保存当前文件对应的所有数据块信息)
private long header = 0L;
// 前4个比特用于保存存储策略,中间12个比特保存文件备份系数,后48个比特用户保存数据块的大小
private BlockInfo[] blocks;
// 保存数据块与文件,数据块与数据节点的对应关系,即获取数据块所属的INodeFile文件和数据块副本的所有数据节点信息
INodeFile的主要方法,构建(Under Construction)/快照(snapshot)等
4.INodeReference类
当HDFS文件/目录处于某个快照中,当这个文件/目录被重命名或者移动到其他路径时,该文件/目录通过快照访问的路径不会断开,仍然可以通过快照访问。如下图:用户可以通过/abc/foo以及/abc/snapshot/s0/foo访问foo文件
当用户将/abc/foo重命名为/xyz/bar时,用户可以通过/xyz/foo以及/abc/snapshot/s0/foo访问foo文件
5.Feature类
Feature主要对应的子类:
1.SnapshotFeature快照特性
快照是一个文件系统或者是某个目录在某一时刻的镜像,当创建目标目录的快照之后,不论目标目录或者目标目录的子目录发生任何变化,都可以通过快照找回快照建立时的目标目录的所有文件及目录结构。
如图下图快照
说明:对目录a创建了快照s1和快照s2,DirectoryDiff记录快照创建之后目录a上执行的所有操作(其中ChildrenDiff对象的c-list集合保存了快照创建之后所有新添加的文件或目录,ChildrenDiff对象的d-list集合保存了快照创建之后所有删除的文件或目录,修改的元素同时放入c-list和d-list)。这样HDFS通过s1访问e时仍然可以访问到,但HDFS通过快照s2访问g时,则会返回空,因为g在c-list中,是新建的文件,表明s2创建时目录下还没有这个g文件。
6.FSEditLog类
NameNode将命名空间(namespace:文件系统的目录树、文件元数据等信息)记录在fsimage的二进制文件中,fsimage将文件系统目录树中的每个文件或者目录的信息保存为一条记录(包括文件或者目录的名称、大小、用户、用户组、修改时间、创建时间等信息)。Namenode重启时会读取fsimage文件来重构命名空间。但是fsimage始终是磁盘上的一个文件,不可能时时刻刻跟进Namenode内存中的数据结构保存同步,而且fsimage文件一般都很大(GB级别很常见),如果所有的更新操作都实时的写入fsimage文件,会导致Namenode运行缓慢,所以HDFS每隔一段时间才更新一次fsimage文件。为解决操作记录实时同步的问题,HDFS客户端执行的所有写操作都会首先记录保存到editlog文件中,HDFS定期地将editlog与fsimage文件合并,以保持fsimage跟Namenode内存中记录的namespace完全同步。
1.logEdit的同步实现:
logEdit成功的获取transactionId(每次递增1)之后,比较这个transactionId和editlog文件中的transactionId,如果当前线程的transactionId大于editlog文件中的transactionId,则表明editlog中的文件记录的不是最新的数据。通过输出流暂存到缓存区中,当logEdit方法将一个完整的操作写入到数据流后,调用logSync方法同步当前线程对editlog文件所做的修改。
clipboard1.png7.FSImage类
主要负责功能:
1.保存Namenode内存中的namespace到fsimage文件中;
2.加载磁盘上的fsimage文件中保存的namespace到Namenode的内存中;
3.加载editlog文件到Namenode内存中。
8.数据块管理:
Namenode定期将文件系统目录树和文件与数据块的对应关系保存到fsimage文件中,但fsimage不会保存数据块和数据节点的对应关系,这部分数据是由Datanode主动将自己保存的数据块信息汇报给Namenode。
clipboard2.png
1. Block类
public class Block implements Writable, Comparable<Block> {
private long blockId; // 唯一标示Block对象
private long numBytes; // 数据块的大小,单位是字节
private long generationStamp; // 这个数据块的时间戳
}
2.BlockIndo类
bc字段保存了该数据块归属于那个HDFS文件,即INode的引用;
triplets保存了数据块的副本存储在那些数据节点上,triplets数据有3 * replication个元素,假设i为第i个保存该数据块副本的Datanode,那么triplets[3*i]是保存这个数据块副本的第i个Datanode上的DatanodeStoreInfo对象
3.BlocksMap类
class BlocksMap {
private final int capacity;
private GSet<Block, BlockInfo> blocks; // 保存数据块与保存这个数据块的数据节点的对应关系,因此可以获取某个数据块对应的HDFS文件,还可以获取数据块保存在那些节点上.
}
8.BlockManager 类
public class BlockManager {
// 损坏的数据块副本集合
final CorruptReplicasMap corruptReplicas = new CorruptReplicasMap();
//等待删除的数据块副本集合
private final InvalidateBlocks invalidateBlocks;
// 推迟操作的数据块副本集合
private final Set<Block> postponedMisreplicatedBlocks = Sets.newHashSet();
//多余的数据块副本集合
public final Map<String, LightWeightLinkedSet<Block>> excessReplicateMap =
new TreeMap<String, LightWeightLinkedSet<Block>>();
// 等待复制的数据块副本集合
public final UnderReplicatedBlocks neededReplications = new UnderReplicatedBlocks();
// 已经生成复制请求的数据块副本
final PendingReplicationBlocks pendingReplications;
}
23rwerae.png
9.数据块汇报:
1.DataNode启动后,会与Namenode握手、注册以及向Namenode发送第一次全量块汇报(即Datanode上存储的所有副本信息),之后Datanode会定期(默认6个小时)向Namenode发送全量汇报,同时会以心跳间隔(默认为3秒)的100倍时间间隔向Namenode发送增量汇报(即Datanode最近新添加的以及删除的副本信息),这样就针对增量汇报异常或者指令丢失的情况发生时,能够保证Namenode获取Datanode保存的所有副本的信息。
为了提高HDFS的启动速度,Namenode会将Datanode的全量汇报分成两种第一次发送全量块汇报(这时Namenode不计算要删除的元数据和无效副本)和周期性的全量块汇报。
10. Datanode的心跳
Datanode会以一定的间隔想Namenode发送心跳信息(Datanode的注册信息、Datanode存储信息、缓存信息、当前Datanode的写文件的连接数、读写数据使用的线程数据等),Namenode收到Datanode的心跳后,返回一个心跳相应,心跳相应中包含了DatanodeCommand的数组,用来携带Namenode对Datanode的指令(数据块副本的复制、删除、缓存等)。
Namenode会启动一个线程,负责周期性地检测所有Datanode上报的心跳情况,对于长时间没有上报心跳的Datanode,会删除该Datanode。
10.HDFS缓存
HDFS机制中缓存是由分布在Datanode上的堆外内存组成,并且有Namenode统一管理。Datanode会会周期性的向Namenode发送缓存报告,而Namenode会通过心跳相应想Datanode下发缓存指令。
11. HDFS的HA架构
HA(High Availability)架构可以解决单节点故障问题,在一个HA集群中,会配置两个独立的Namenode。任意时刻,一个节点作为活动节点,另外一个处理备份状态。活动的Namenode负责执行所有的修改namespace和删除备份数据块的操作,而备份的Namenode则执行同步操作,保持与活动节点namespace的一致性(通过监听editlog改动,读取并合并到当前的namespace中)。另外,所有的Namenode会同时向这两个Namenode发送心跳以及块汇报信息(备份Namenode不会向Datanode发送指令(数据块副本的复制、删除、缓存等)),这样活动NameNode和备份Namenode的元数据就会完全同步了,实现了热备份。
四、Datanode
werweard.png未完待续......