好程序员大数据学习路线分享分布式文件系统HDFS,设计目标:
1、存储量大
2、自动快速检测应对硬件错误
3、流式访问数据
4、移动计算比移动数据本身更划算
5、简单一致性模型
6、异构平台可移植
特点
优点:
- 高可靠性:Hadoop按位存储和处理数据的能力强
- 高扩展性:hadoop是在可用的计算机集簇间分配数据并完成计算任务的,这些集簇可以方便地扩展到数以千计的节点中
- 高效性:hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快
- 高容错性:hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配。
缺点:
- 不适合低延迟数据访问
- 无法高效存储大量小文件(每个文件存储都有属于自己的索引,元数据庞大就不高效)
- 不支持多用户写入及任意修改文件(可以删除以及追加,只是不能修改文件中某处的数据)
重要特性:
- 文件在物理上是分块存储,块的大小可以通过配置参数(dfs.blocksize)来规定,默认2.x版本之后是128M,老版本是64M
- HDFS会给哭护短提供一个统一的抽象目录树,客户端通过路径来访问文件,刑辱:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data
- 目录结构及文件分块信息(元数据)的管理由namenode承担—namenode是HDFS集群主节点,负责维护整个hdfs文件系统的目录树,以及每一个路径(文件)所对应的block块信息(block的id以及所在datanode服务器)
- 文件的各个block的存储管理由datanode承担—datanode是HDFS集群从节点,每一个block都可以在多个datanode上存储多个副本(副本参数设置dfs.replication)
- HDFS是设计成适应一次写入,多次读出的场景,且不支持文件的修改
- 管理文件系统的命名空间(元数据<metadata>:包含文件名称、大小、所属人、地址)
- 规定客户端访问文件规则
三个服务
Namenode
任务清单
a) 文件大小是否已经超载(超过集群的负载)
b) 是否已经存在相同的文件
c) 是否具有创建该文件的权限
- 对文件执行命令,关闭,打开文件或者打开路径等操作
- 所有的数据节点发送心跳给NameNode,他需要确保数据节点DataNode是否在线,一个数据块报告包含所有这个数据节点上的所有block的状况
- 首先将fsimage(镜像)载入内存,并读取执行日志editlog的各项操作
- 一旦在内存中建立文件系统元数据映射,则创建一个新的fsimage文件(这个过程不需要secondaryNamenode)和一个空的editlog
- 在安全模式下,各个datanode会向namenode发送块列表的最新情况
- 此刻namenode运行在安全模式。即NameNode的文件系统对于客户端来说是只读
- NameNode开始监听RPC和HTTP请求
启动过程
RPC:Remote Procedure Call Protocol---远程过程通过协议
它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
- 系统中数据块的位置并不是由namenode维护的,而是以块列表形式存储在datanode中
- 在系统的正常操作期间,namenode会在内存中保留所有块信息的映射信息
- fsimage:元数据镜像文件(保存文件系统的目录树)
- edit.log:元数据操作日志(针对目录树的修改操作)
两个重要文件
元数据镜像
a) 内存中保存一份最新的
b) 内存中镜像=fsimage+edits
SecondaryNamenode
工作任务
定期合并fsimage与edits
c) Edits文件过大将导致NamenNode重启速度缓慢
d) SecondaryNameNode负责定期合并他们
Datanode
hdfs的写过程
写过程语言Description:
- Client通过调用FileSystem的get方法与namenode进程建立通道进行通信,然后调用create方法来请求创建文件。
- FileSystem通过对namenode发出远程请求,在namenode里面创建一个新的文件,但此时并不关联任何的块。NameNode进行很多检查来保证不存在要创建的文件已经存在文件系统中,同时检查是否有相应的权限来创建文件。如果这些检查完了,nameNameNode将这个新文件的嘻嘻记录下来,然后FileSystem返回一个DFSOutputStream给客户端用来写入数据。和读的情形一样,FSDataOutputStream将包装一个DFSOutputStream用于和DataNode及NameNode通信。而一旦文件创建失败,客户端会受到一个IOException,标识文件创建失败,停止后续任务。
- 客户端开始写数。FSDataOutputStream把要写入的数据分成块打成包的形式,将其写入到DFSOutputStream对象的中间队列中。其中的数据由Datastreamer来读取。DataStreamer的职责是让NameNode分配新的块—找出合适的DataNode来存储作为备份而复制的数据。
- FSDataOutputStream维护了一个内部关于packets的队列,里面存放等待被DataNode确认无误的packets的信息。这个队列被称为等待队列,一个packet的信息被移出本队列当且仅当packet被所有节点都确认无误。
- 当完成数据写入之后客户端调用流的close方法,再通知NameNode完成写入之前,这个方法将flush残留的packets,并等待确认信息。NameNode已经知道文件由哪些块组成,所以在返回成功前只需要等待数据块进行最小复制。
Write API:
1.从本地系统上传到hdfs
Configuration hdfsConf = new Configuration();//创建一个hdfs的环境变量
String namenodeURI=”hdfs://hadoop001:8020”;//namenode的统一资源定位符
String username=”root”;//访问指定用户的hdfs
FileSystem hdfs = FileSystem.get(new URI(namenodeURI),hdfsConf,username);//创建一个hdfs的文件系统对象
FileSystem local = FileSystem.getLocal(new Configuration());//创建一个本地的文件系统对象
hdfs.copyFromLocalFile(new Path(localPath),new Path(hdfsPath));
2.在hdfs上创建文件并直接给定文件的内容
FSDateOutputStream out = hdfs.create(new Path(hdfsPath));
out.write(fileContent.getBytes());
out.close();
hdfs的读过程
读过程语言Description:
- 客户端或者用户通过调用FileSystem对象的open方法打开需要读取的文件,这对HDFS来说是常见一个分布式文件系统的一个读取实例。
- FileSystem通过远程协议调用NameNode确定文件的前几个Block的位置。对于每一个Block,Namenode返回含有那个Block 的“元数据”,即文件基本信息;接下来,DataNode按照上文定义的距离来进行排序,如果Client本身就是一个DataNode优先从本地DataNode读物数据。HDFS实例完成以上工作后,返回一个FSDataInputStream给客户端,让其从FSDataInputStream中读取数据。FSDataInputStream接着包装一个DFSInputStream用来管理DataNode和NameNode的I/O。
- NameNode向客户端返回一个包含数据信息的地址,客户端格努诋毁创建一个FSDataInputStream开始对数据进行读取。
- FSDataInputStream根据开始存放的前几个Blocks的DataNode的地址,连接到最近的DataNode上对数据开始从头读取。客户端反复调用read()方法,以流式方式从DataNode读取数据
- 当读到Block的结尾的时候,FSDataInputStream会关闭当前DataNode的地址,然后查找能够读取下一个Block的最好的DataNode。这些操作对客户端是透明的,客户端感觉到的是连续的流,也就是说读取的时候就开始查找下一个块所在的地址。
- 读取完成调用close()方法,关闭FSDataInputStream。
Read API:
1.从hdfs上下载文件到本地
Configuration hdfsConf = new Configuration();//创建一个hdfs的环境变量
String namenodeURI=”hdfs://hadoop001:8020”;//namenode的统一资源定位符
String username=”root”;//访问指定用户的hdfs
FileSystem hdfs = FileSystem.get(new URI(namenodeURI),hdfsConf,username);//创建一个hdfs的文件系统对象
FileSystem local = FileSystem.getLocal(new Configuration());//创建一个本地的文件系统对象
hdfs.copyToLocalFile(new Path(hdfsPath),new Path(localPath));
3. 在hdfs上读取给定文件的内容
Path path = new Path(hdfsFilePath);//文件路径
FSDataInputStream in = hdfs.open(path);//获取文件输入流
FileStatus status = hdfs.getFileStatus(path);//获取文件的元数据信息
//获取文件元数据中的文件大小
byte[] bytes = new byte[Integer.pareInt(String.valueOf(status.getLen()))];
//将输入流中的全部内容一次性读取出来
in.readFully(0,bytes);
System.out.println(new String(bytes));//将读取的文件打印输出
in.close();