Hadoop权威指南-HDFS概述

来源:互联网 发布:变声软件变成女人声 编辑:程序博客网 时间:2024/05/29 15:26

第6章HDFS概述

6.1HDFS的主要特性

1.HDFS的主要特征

l  支持超大文件

l  检测和快速应对硬件故障

l  流式数据访问

l  简化一致性模型(1次写多次读)

 

2.HDFS不适合的场景

l  低延迟数据访问,可以考虑HBase或者Cassandra

l  大量小文件

l  多用户写入文件、修改文件

 

3.HDFS体系结构(Master/Slave)

由Namenode,SecondaryNameNode,DataNode,Client四部分组成

l  数据块

HDFS默认的数据块是64MB,使用数据块的好处

1) HDFS可以保存比存储节点单一磁盘大的文件

2) 简化了存储子系统

3) 方便容错,有利于数据复制(3副本方式)

l  NameNode和SecondaryNameNode

Namenode:维护整个文件系统的文件目录树,文件/目录的元信息和文件的数据块索引,即每个文件对应的数据块列表,这些信息存储在本地文件系统:一种是命名空间镜像(File System Image,FSImage,保存某一时刻HDFS的目录树,元信息,数据块索引),另一种是命名空间镜像的编辑日志(Edit Log),由这两种构成了完整的NameNode第一关系

通过NameNode,Client可以了解到数据块所在的数据节点信息,NameNode每次启动后便会动态地构建这些信息,此为NameNode的第二关系

除此之外,Namenode还能获得HDFS运行状态,包括系统的可用空间,已经使用的空间,各数据节点的当前状态等等

 

SecondaryNameNode用于定期合并命名空间镜像和镜像编辑日志的辅助守护进程

 

l  DataNode(将HDFS数据块写到Linux本地文件系统的实际文件中,或者从这些实际的文件中读取数据块)

 

客户端读取数据块过程:

1) 客户端请求NameNode获得请求数据块所在的DataNode信息

2) 根据DataNode守护进程,处理数据块对应的Linux本地文件

3) DataNode与其他DataNode通信,复制数据块保证冗余性

 

l  Client

HDFS提供了多种Client接口包括命令行,Java API,Thrift接口,C语言库,用户空间文件系统

1)     命令行

HDFS文件系统创建目录:

hadoop fs –mkdir testDIR

本地文件上传到HDFS:

hadoop fs –copyFromLocaltestInput/hello.txt  /user/alice/in/hello.txt

 

2)Java API(本地数据写入HDFS文件)

Path path=newPath(“hdfs://ip:port/user/alice/in/hello.txt”);

FileSystemhdfs=FileSystem.get(path.toUri,conf);

FSDataOutputStream fout=hdfs.create(path);

String data=”testingtesing”;

for(int i=0;i<256;i++){

fout.write(data.getBytes());

}

fout.close();

FileStatus stat=hdfs.getFileStatus(path);

H                                                                dfs.delete(path);

 

3.HDFS源码结构

l  hdfs.security.token.block和hdfs.security.token.delegation结合Hadoop的安全框架提供安全访问HDFS的机制

l  hdfs.server.common包含Namenode和DataNode共享功能(系统升级、存储空间)

l  hdfs.protocol 提供HDFS实体间IPC交互的接口

l  hdfs.server.namenode、hdfs.server.datanode、hdfs包含了NameNode、DataNode和Client的实现

l  hdfs.server.namenode.metrics和hdfs.server.datanode.metrics实现了Namenode和DataNode上度量数据(写入字节数、被复制块数量)的收集功能

l  hdfs.toolls和hdfs.server.balancer提供查询HDFS状态信息的工具dfsadmin、文件系统检查工具fsck和HDFS均衡器balancer

l  hdfs.web.resources,hdfs.server.namenode.web.resources,hdfs.server.datanode.web.resources,hdfs.web提供了WebHDFS即通过HTTP访问HDFS的机制,实现了C客户端和用户空间文件系统(FUSE)

 

6.2 基于远程过程调用的接口

NameNode,DataNode,Client之间通信接口包括:

1) Hadoop远程过程调用接口

2) 基于TCP或者HTTP的流式接口

上述接口又分为三大类

1) 客户端相关的接口(org.apache.hadoop.hdfs.protocol)

Ø  ClientProtocol:客户端与Namenode之间的接口

Ø  ClientDatanodeProtocol:客户端与DataNode之间的接口,客户端与数据节点之间的交互主要通过流接口进行读/写文件数据的操作,错误发生时客户端配合数据节点进行恢复,当客户端继续进行本地文件读优化时,需要通过IPC接口获取一些信息

2) 服务器间的接口(org.apache.hadoop.hdfs.server.protocol)

NameNode、SecondaryNameNode、DataNode之间的IPC调用关系

Ø  DatanodeProtocol:DataNode与NameNode之间的接口,HDFS的主从体系结构中,由从节点DataNode不断通过此接口向主节点NameNode报告信息,同时有些方法的返回值会待会NameNode指令,根据这些指令数据节点或者移动或者删除或者恢复本地磁盘上的数据块,或者执行其他操作

Ø  InterDatanodeProtocol:DataNode与DataNode之间的接口,用于与其他DataNode进行通信,恢复数据块保证数据的一致性

Ø  NamenodeProtocol:SecondaryNameNode,HDFS Balancer与Namenode之间的接口,主要是SecondaryNameNode不断合并本地命名空间镜像和编辑日志得到新的命名空间镜像通过此接口上传至Namenode,也可以为HDFS Balancer提供配置信息

3) 与安全有关的接口(org.apache.hadoop.security.authorize和org.apache.hadoop.security)

 

1.    与客户端有关的接口

ClientProtocol和ClientDatanodeProtocol

与数据块相关、与数据节点相关、和HDFS文件元数据相关

1) 与数据块相关

数据块的类是org.apache.hadoop.hdfs.protocol.Block,包含3个成员变量blockId(数据块标识),numBytes(文件数据大小),generationStamp(版本号,一致性检查),blockName为blk_blockId

  

   数据块的存储位置LocatedBlock

   ClassLocatedBlock{

   Block b;//指定数据块

   long offset;//数据块在本地文件中的偏移量

   DatanodeInfo[]locs;//数据块在哪些Datanode上

   boolean corrupt;//数据块是否损坏

}

多个数据块的存储位置LocatedBlocks

class LocatedBlocks{

LocatedBlock[] locatedBlocks;

long fileLength; //对应文件长度

boolean underConstruction;//该文件是够处于构建状态标志

}

 

BlockLocalPathInfo应用于ClientDatanodeProtocol中,,用于优化HDFS读文件的数据节点本地读优化,当客户端发现它与它读取的数据块在同一台主机上时,可以直接读取本地文件,Client访问ClientDatanodeProtocol得到本地文件,Datanode通过BlockLocalPathInfo得到本地文件路径保存在localBlockPath中,成员变量localMetaPath保存数据块的校验信息,根据此两种信息便可以直接访问本地文件

 

2) DatanodeID和DatanodeInfo

public class DatanodeID implementsComparable<DatanodeID> {

  private StringipAddr;     // IP address

  private StringhostName;   // hostname claimed bydatanode

  private StringpeerHostName; // hostname from the actual connection

  private intxferPort;      // data streaming port

  private intinfoPort;      // info server port

  private intinfoSecurePort; // info server port

  private intipcPort;       // IPC server port

}

public class DatanodeInfo extends DatanodeID implementsNode {

  private longcapacity;//Datanode总容量

  private longdfsUsed;//Datanode已使用的容量

  private longremaining;//Datanode剩余容量

  private longblockPoolUsed;//已使用的BlockPool

  private longcacheCapacity;//缓存总容量

  private longcacheUsed;//缓存已使用容量

  private longlastUpdate;//状态更新时间

  private longlastUpdateMonotonic;

  private intxceiverCount;//流接口服务线程数

  private Stringlocation = NetworkTopology.DEFAULT_RACK;

  private String softwareVersion;//版本控制

  privateList<String> dependentHostNames = new LinkedList<String>();

protected AdminStates adminState;//Datanode状态

}

 

3) HdfsFileStatus(保存HDFS文件/目录的属性)和DirectoryListing(返回一个目录下的多个文件/子目录)

 

系统研究与客户端相关的接口ClientProtocol,其提供了两大功能:

1) 实现Hadoop文件系统的相关功能,包括文件/目录元数据部分操作,写文件操作,读文件操作

读文件操作有getBlockLocations(),reportBadBlocks()

写文件操作有打开文件的create()和append(),追加数据块的增addBlock()和删abandonBlock(),持久化文件fsync(),关闭文件complete(),租约相关renerLease()和recoverLease()

 

2) 对HDFS状态进行查询、设置(dfsadmin)

 

ClientProtocol实现文件系统的相关操作API:

1)    文件/目录相关事务的方法,与Hadoop抽象文件系统的方法相差不大

2)    写数据的方法:

public HdfsFileStatus create(

String src, //文件路径

FsPermission masked,//文件权限

StringclientName, //客户端名字

EnumSetWritable<CreateFlag>flag,//

booleancreateParent, //是否创建不存在的父目录

Boolean overwrite;//是否覆盖已存在的文件

shortreplication, //副本数

longblockSize, //数据块大小

CryptoProtocolVersion[]supportedVersions)//支持版本·

 

publicLastBlockWithStatus append(

Stringsrc,

StringclientName,

Create()和append()方法都是打开文件的API操作

 

@Idempotent

publicLocatedBlock addBlock(//写数据块,返回数据块的位置

Stringsrc, //文件路径

StringclientName,

ExtendedBlockprevious,//旧块

DatanodeInfo[]excludeNodes, //选择数据目标时排除某些数据节点

longfileId, //唯一标识文件

String[]favoredNodes)//客户端想要数据块在哪些节点

 

AbandonBlock()//用于放弃一个数据块,比如第一次写数据块到某个节点,而节点发生故障,则需要调用abandonBlock放弃该数据块,然后调用addBlock()重新添加此数据块但需要将上轮故障节点的信息放入excludedNodes中

 

@Idempotent

publicvoid fsync(//保证Namenode对于文件元数据的修改保存在磁盘

Stringsrc,

longinodeId,

Stringclient,

longlastBlockLength)

 

complete(String src,String clientName)//关闭输出流

 

ClientProtocol.renewLease()//客户端向Namenode发送心跳信息,如果Namenode长时间未收到客户端的租约更新,可以认为客户端已经崩溃,Namenode会试图关闭文件防止信息泄露

 

ClientProtocol.recoverLease()//若返回true则表示src指定的文件已被正常关闭,客户端可以通过append()打开文件,继续写数据

 

若Namenode从故障中恢复,则根据可根据编辑日志回复Namenode的租约信息

 

3) 读数据

publicLocatedBlocks getBlockLocations(//返回一系列的LocatedBlock对象,客户端根据这些对象访问数据节点获得文件数据

String src,//文件路径

long offset,//文件偏移

long length) //长度

 

   @Idempotent

 public void reportBadBlocks(LocatedBlock[] blocks) throws IOException;//当发现输入数据有错时,客户端调用此方法向Namenode报告,blocks为错误块

 

4) 工具dfsadmin(供HDFS管理员使用)

@Idempotent

public void setQuota(//设置配额,其中配额有对目录树中的文件/目录数量限制;对目录树中存储的文件大小限制

String path, //路径

long namespaceQuota,//目录配额

long storagespaceQuota,//空间配额

StorageType type)

 

@Idempotent

public boolean setSafeMode(//设置HDFS安全模式

HdfsConstants.SafeModeAction action, //进入/退出模式/状态查询

boolean isChecked)

 

@AtMostOnce

 public void saveNamespace()//当前内存中的文件系统镜像保存到新的命名空间镜像中并重置日志文件

 

@Idempotent

 public void metaSave(String filename) //将Namenode主要数据结构保存在Hadoop日志目录的指定文件中

 

@Idempotent

 public void refreshNodes() //更新数据节点的设置,所需要的信息保存在include文件(配置目录下指定链接到Namenode的Datanode列表,exclude文件指定要移除的Datanodes

 

distributedUpgradeProgress()查询升级进度,finalizeUpgrade()提交升级

 

ClientDatanodeProtocol与客户端相关的API操作

1) recoverBlock()//进行数据块恢复

2) getBlockInfo()//与HDFS一致性模型有关,返回指定块信息

3) getBlockLocalPathInfo()//返回指定数据块对应的本地文件路径和校验信息路径

 

2.    与服务器相关的接口

Ø  DatanodeRegistration和NamespaceInfo

public classDatanodeRegistration extends DatanodeID//Datanode注册

    implements NodeRegistration {

  private final StorageInfo storageInfo;//存储系统信息

  private ExportedBlockKeys exportedKeys;

  private final String softwareVersion;

  private NamespaceInfo nsInfo;

  }

 

public class StorageInfo {

  publicint   layoutVersion;   // 存储系统结构的版本号

 public int   namespaceID;     // 存储系统的唯一标识

 public String clusterID;      // 集群ID

 public long  cTime;           // 存储系统信息的创建时间

 protected final NodeType storageType; // 使用存储系统的节点类型

}

 

public class NamespaceInfo extendsStorageInfo {//整个集群的信息

 final String  buildVersion;//系统构建的版本号

 String blockPoolID = "";   // id of the block pool

 String softwareVersion;

 long capabilities;

}

 

Ø  握手、注册、数据块上报和心跳

1) 握手

Datanode启动时,会和Namenode握手,使用DatanodeProtocol.versionRequest(),若是升级启动,则不会进行后续注册操作,且握手时会检查Namenode和Datanode的buildVersion

2) 注册

Datanode调用远程方法DatanodeProtocol.register()向Namenode注册;当注册成功后Datanode调用DatanodeProtocol.blockReport()成员方法,上报其所管理的所有数据块信息,有助于Namenode建立数据块->Datanode的映射关系

@Idempotent

  public DatanodeCommand blockReport(//返回Namenode的指令(重新注册,发送心跳或者删除本地磁盘的数据块)

DatanodeRegistrationregistration,//Datanode注册信息

StringpoolId,

StorageBlockReport[]reports,//上报的数据块信息

BlockReportContext context)

 

Datanode定期向Namenode发送心跳检测

@Idempotent

  public HeartbeatResponse sendHeartbeat(//返回一系列Namenode命令

DatanodeRegistrationregistration,

StorageReport[]reports,

longdnCacheCapacity,

longdnCacheUsed,

intxmitsInProgress,

intxceiverCount,

intfailedVolumes,

VolumeFailureSummaryvolumeFailureSummary)

 

publicclass HeartbeatResponse {

  /** Commands returned from the namenode tothe datanode */

  private final DatanodeCommand[] commands;

 

  /** Information about the current HA-relatedstate of the NN */

  private final NNHAStatusHeartbeat haStatus;

 

  private finalRollingUpgradeStatus rollingUpdateStatus;

}

 

3)    Namenode命令

@InterfaceAudience.Private

@InterfaceStability.Evolving

publicabstract class DatanodeCommand extends ServerCommand {

 

  DatanodeCommand(int action) {

    super(action);

  }

}

Action的取值包括

finalstatic int DNA_UNKNOWN = 0;    // unknownaction  

finalstatic int DNA_TRANSFER = 1;   //transfer blocks to another datanode

finalstatic int DNA_INVALIDATE = 2; // invalidate blocks

finalstatic int DNA_SHUTDOWN = 3;   //shutdown node

finalstatic int DNA_REGISTER = 4;   //re-register

final staticint DNA_FINALIZE = 5;   // finalizeprevious upgrade

finalstatic int DNA_RECOVERBLOCK = 6;  //request a block recovery

finalstatic int DNA_ACCESSKEYUPDATE = 7;  //update access key

finalstatic int DNA_BALANCERBANDWIDTHUPDATE = 8; // update balancer bandwidth

finalstatic int DNA_CACHE = 9;      // cacheblocks

finalstatic int DNA_UNCACHE = 10;   // uncacheblocks

       除了和升级相关的两个指令:DatanodeCommand.Finalize和UpgradeCommand.,其他都由心跳检测携带

 

4)    数据块操作方法reportBadBlocks()

Datanode一旦通过CRC-32检测到错误块后,调用DatanodeProtocol.reportBadBlocks()//上报错误块到Namenode,Namenode向Datanode发出移除错误块的指令

 

Datanode完整接收数据块后向Namenode提交数据块

@Idempotent

  public void blockReceivedAndDeleted(

DatanodeRegistrationregistration,

StringpoolId,

StorageReceivedDeletedBlocks[]rcvdAndDeletedBlocks)

 

当系统支持append时,即往HDFS文件中追加数据时,Datanode注册后调用DatanodeProcotol.blocksBeingWrittenReport()向Namenode报告正在处于写状态的数据块信息,以帮助Namenode进行租约恢复

 

当Namenode进行租约恢复时,由Datanode配合进行数据块恢复,或者客户端调用ClientDatanodeProtocol接口的recoverBlock()方法,处理写数据过程中的Datanode错误,启动数据块恢复时,都需要使用DatanodeProtocol.nextGenerationStamp()和commitBlockSynchronization()方法,nextGenerationStamp()用于向Namenode申请一个新的数据块版本号,无论恢复结果如何,都需要通过commitBlockSynchronization()告知数据块恢复的执行情况

 

4) ErroReport()和processUpgradeCommand()

ErroReport()用于Datanode向Namenode上报运行过程的一些状况

ProcessUpgradeCommand()与系统升级有关,向Namenode报告升级状态

 

3.    IntelDatanodeProtocol

StartBlockRecovery()获得参与数据块恢复过程中的各个Datanode上的数据块信息

UpdateBlock()通知其他Datanode将各个节点上的待恢复数据块更新至该长度,并更新数据块的版本号

4.    NamenodeProtocol

@Idempotent

  public BlocksWithLocations getBlocks(DatanodeInfodatanode, long size)//返回某个Datanode上的所有数据块和位置,均衡器会根据这些信息进行负载均衡

 

@Idempotent

  public ExportedBlockKeys getBlockKeys() //支持安全特性

 

其他接口方法用于实现SecondaryNamenode功能

 

6.3 非远程过程调用(TCP流式数据访问)

Datanode提供读写功能,流式数据访问接口操作码定义在DataTransferProtocol中

Ø  读数据

请求帧分别为TRANSFER_VERSION(接口版本号),操作码,数据块ID,数据块版本号,偏移量,数据长度,客户端名字,访问令牌

 

响应头分别为操作返回码,数据校验的类型,校验和对应的数据块大小,偏移量;紧接着的是多个数据包,分别为包长度,偏移量,顺序号,是否为最后一个包,数据长度,校验数据,数据

Ø  写数据

请求帧分别为接口版本号,操作码,数据块ID,时间戳,数据流管道大小,是否属于数据恢复过程,客户端名字,是否携带源信息,源信息,数据目标列表大小,数据目标列表,访问令牌,数据校验信息

 

响应头分别为返回码和附加信息,当客户端接收到成功的响应头后会继续发送一个或者多个写数据包(与读响应包结构相同),附加信息会反馈第一个联系不上的Datanode

 

在写数据过程中,下游节点写成功后需要向上游节点发送确认包DataTransferProtocol.PipelineAck对象序列化的结果,以释放相应的缓冲区内容,其中PipelineAck中包含seqno(确认对应的写请求数据包),replies包含各节点对写请求数据包的处理结果

Ø  其他接口

数据块替换:操作码83

数据块替换请求帧分别为接口版本号,操作码,数据块ID,版本号,数据源的存储标识,源数据节点,访问令牌

 

数据块拷贝:操作码84

数据块拷贝请求帧分别为接口版本号,操作码,数据块ID,版本号,访问令牌

 

读数据块校验信息:操作码85,仅读取MD5摘要

读数据块校验信息请求帧分别为接口版本号,数据块ID,版本号,访问令牌

读数据块校验信息应答帧分别为响应码,校验和对应的块大小,CRC的个数,MD5结果

 

6.4 Namenode与SecondaryNamenode上的非IPC接口(基于HTTP协议)

SecondaryNamenode获取命名空间镜像和编辑日志

http://名字节点地址:名字节点HTTP端口/getimage?getimage=1;//获得命名空间镜像

http://名字节点地址:名字节点HTTP端口/getimage?getedit=1;//获得编辑日志

由于合并需要时间,因此SecondaryNamenode通知Namenode数据准备好了

http://名字节点地址:名字节点HTTP端口/getimage?putimage=1&port=第二名字节点HTTP端口&machine=第二名字节点地址&token=校验信息

http://第二名字节点地址:第二名字节点HTTP端口/getimage?getimage=1获得新的命名空间镜像

URL url=new URL(str.toString);

URLConnection connection=url.openConnection();

InputStream stream=connection.getInputStream();

Hadoop各个节点内嵌Jetty(轻量Web容器)

 

6.4 HDFS主要流程

Ø  以创建目录为例:

1)    HDFS Client调用FileSystem实例(DistributedFileSystem)的mkdir()

2)    DistributedFileSystem通过IPC调用Namenode的远程方法mkdir()

3)    Namenode执行具体创建子目录操作,在目录树数据结构上的对应位置创建新的目录节点,同时记录这个操作并持久化到日志中

 

Ø  以客户端删除HDFS文件为例

1)HDFS Client调用FileSystem实例(DistributedFileSystem)的delete()

2)DistributedFileSystem通过IPC调用Namenode的远程方法delete()

3) Namenode执行具体delete()方法时,它只标记设计需要被删除的数据块(同时保存delete操作在日志中)

4)DatanodesendHeartbeat()发送心跳检测,在返回值DatanodeCommand中命令Datanode删除数据

 

Ø  客户端读文件

1) HDFS Client调用FileSystem实例(DistributedFileSystem)open()打开文件并创建输入流FSDataInputStream(DFSInputStream)

2) 在DFSInputStream的构造函数中,ClientProtocol.getBlockLocations()远程接口调用Namenode,以确定文件开始部分数据块的保存地址,以及其副本的数据节点地址(排序)

3) Client调用FSDataInputStream.read()与最近的数据节点的读数据流接口建立联系

4) 当到达块末端时,DFSInputStream会关闭和数据节点间的连接,并通过getBlockLocations的远程方法得到保存着下个数据块的数据节点信息

 

错误处理:

1) 客户端读取文件时,若数据节点发送错误,客户端尝试读取下一个数据块位置,记录该故障的数据节点

2) 读数据的应答包中包含数据校验和,,若发现校验错误,则将该信息报告给Namenode,同时尝试读取另外一个节点上保存的副本

 

Ø  客户端写文件

1)    HDFS Client调用DistributedFileSystem的create()创建文件

2)    DistributedFileSystem创建DFSOutputStream,并通过远程调用Namenode的create()在命名空间创建一个新文件,并将记录创建操作到编辑日志中,远程方法调用后,DFSOutputStream包装在FSDataOutputStream中返回给客户端

3)    DFSOutputStream.addBlock()向Namenode申请数据块,并返回LocatedBlock对象,DFSOutputStream就可以和数据节点联系写数据接口建立数据流管道

4)    客户端写入FSDataOutputStream流中的数据便分成多个文件包放入DFSOutputStream对象的内部队列中,该队列中的文件包打包成数据包发往数据流管道,流经管道上的各个Datanode并持久化,确认客户端收到应答后将此包从内部队列中删除

5)    每写完一个数据块,数据流管道上的节点会通知Namenode的DatanodeProtocol的远程调用接口blockReceived(),向Namenode提交数据块

6)    客户端完成数据写入后,调用close()关闭流,当数据队列为空时,调用ClientProtocol.complete()通知Namenode关闭文件

 

 

 

 

 



0 0
原创粉丝点击