大数据之路(一)——HDFS分布式文件系统

HDFS介绍

HDFSHDFS是一个主/从(Mater/Slave)体系结构,从最终用户的角度来看,它就像传统的文件系统一样,可以通过目录路径对文件执行CRUD。HDFS一般是用来“一次写入,多次读取”,不适合做实时交互性很强的事情,不适合存储大量小文件。HDFS实现将文件分布式存储在很多服务器上, 其功能是帮用户管理文件。其顶层目录为"/",用户可以将文件存放在相关目录下。例如:/data/2019-01-01/log。当我们向HDFS的目录下存放文件时,其内部会将文件存放到若干台装了“Datanode”的linux服务器上。HDFS提供了一个统一的目录树来定位HDFS中的文件,客户端访问文件时只要指定目录树的路径即可,不用关心文件的具体物理位置.每一个文件的每一个切块,在HDFS集群中都可以保存多个备份默认3份,在hdfs-site.xml中,dfs.replication的value的数量就是备份的数量.

HDFS工作机制

整个HDFS集群由Namenode和Datanode构成master-worker(主从)模式。这种模式主要由四个部分组成,分别为HDFS Client、NameNode、DataNode和Secondary NameNode。

Client:就是客户端。
主要负责文件切分。文件上传 HDFS 的时候,Client 将文件切分成 一个一个的Block,然后进行存储;与 NameNode 交互,获取文件的位置信息;与 DataNode 交互,读取或者写入数据;Client 提供一些命令来管理 HDFS,比如启动或者关闭HDFS;Client 可以通过一些命令来访问 HDFS。
Namenode:就是master。
它维护了一个HDFS的目录树及HDFS目录结构与文件真实存储位置的映射关系(元数据),主要负责管理文件的元数据等。
Datanode:就是slave。
专门负责接收和管理"文件块"-block.*默认大小为128M(可配置),(dfs.blocksize).Datanode存储文件块时是存储在Datanode服务器的本地磁盘上。(老版本的hadoop的默认block是64M)。负责实际存储数据,负责读写工作。
Secondary NameNode:并非 NameNode 的热备。
主要负责辅助 NameNode,分担其工作量;定期合并 fsimage和fsedits,并推送给NameNode;在紧急情况下,可辅助恢复 NameNode。当NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务。

机制总结:用户的文件会被切块后存储在多个Datanode服务器中,并且每个切块都会在整个集群中存在多个副本,副本的数量可以由用户指定。

HDFS客户端常用命令

创建目录:hadoop fs -mkidr /aaa
查看文件:hadoop fs -ls /
移动(更改)HDFS中的文件:hadoop fs -mv /hdfs的路径1 /hdfs的另一个路径2
删除HDFS中的文件或文件夹:hadoop fs -rm -r /aaa
查看HDFS中的文本文件内容: hadoop fs -cat /dat.txt 或者 hadoop fs -tail -f /dat.txt
复制HDFS中的文件到另一个目录中:hadoop fs -cp /hdfs路径1 /hdfs路径2
上传本地文件到HDFS目录中:hadoop fs -put 本地路径 /hdfs路径
HDFS获取目录中的文件到本地:hadoop fs -get /hdfs路径 本地路径

HDFS读取文件中的内容流程

在这里插入图片描述
步骤1:客户端调用FileSystem的get()方法得到一个实例fs(即分布式文件系统DistributedFileSystem),然后fs调用open()打开希望读取的文件;

步骤2:DistributedFileSystem(fs)通过使用RPC调用NameNode以确定文件起始block的位置。(Block位置信息存储在namenode的内存中)。对于每一个bolck,NameNode返回block所有复本的DataNode地址(并根据与client的距离排序)

步骤3:DistributedFileSystem(fs).open()返回一个FSDataInputStream对象给client用来读数据。FSDataInputStream封装了分布式文件输入流(DFSInputStream)用于管理NameNode和DataNode的I/O. client对这个输入流调用read()方法。

步骤4:此输入流DFSInputStream has stored the datanode addresses for the first few blocks in the file, then connects to the first(closest) datanode for the first block in the file.通过对数据流反复的调用read()可以将数据从datanode传输到client。

步骤5:对一个block读完时DFSInputStream会关闭与datanode的连接,然后寻找下一个block的最佳datanode。当一批blocks读完时,DFSInputStream会询问namenode下一批所需blocks的datanode地址。读取blocks的切换对于client是透明的。

步骤6:当client完成读取,调用FSDataInputStream的close()方法。

// 定义一个fileSystem的变量,用的是Hadoop的包
    FileSystem fileSystem = null;
    
    // 添加一个注解
    @Before
    public void getfileSystem() throws IOException, InterruptedException, URISyntaxException{
        
        // 获取一个具体的文件系统对象
        fileSystem = FileSystem.get(
            // 创建一下HDFS文件系统的访问路径,就是Hadoop配置文件中的core-sit.xml中的HDFS文件系统的所在机器
                new URI("hdfs://linux.centos.one:8020"),    
            // 创建一个Hadoop的配置文件的类
                new  Configuration(),
            // 就是Linux启动的用户名
                "hadoop");
    }
    
    /**
     *  从HDFS文件系统下载文件
     *      首先编写一个输入流,将内容输入到本地文件缓存。
     *      然后编写一个输出流,将内容输出到本地磁盘。
     */  
    /**  
     * @throws IOException 
     * @throws IllegalArgumentException 
     */
    @Test
    public void testDownload() throws IllegalArgumentException, IOException{
        
        // 构建一个输入流,将需要下载的文件写入到客户端的内存中
        FSDataInputStream in = fileSystem.open(new Path("/user/beifeng/mapreduce/wordcount/input/wc.input"));
        
        // 构建一个输出流,将需要下载的文件从内存中写入到本地磁盘
        FileOutputStream out = new FileOutputStream("D://11111111111111111111111111111111111111.txt");
        
        /**
         * 参数说明:
         *     in 
         *         代表输入流,读取HDFS文件系统的文件到本机内存中
         *     out
         *         代表输出流,将本机内存中的文件写入到本地磁盘中
         *     4096
         *         缓冲区大小
         *     true    
         *         自动关闭流,如果不使用自动关闭的话需要手动关闭输入输出流
         *         
         *         手动关闭输入输出流:
         *             IOUtils.closeStream(in);
         *            IOUtils.closeStream(out);
         */
        IOUtils.copyBytes(in, out, 4096, true);
        
    }

注意:在读的过程中,如果DFSInputStream和datanode通信时出错,它会尝试连接下一个最近的datanode。DFSInputStream也会通过校验和确认从datanode读取的数据是否完整,如果发现某个block有损坏,就回报告给namenode,然后从其它复本读取此block。

HDFS写文件流程

在这里插入图片描述
步骤1:客户端调用FileSystem的get()方法得到一个实例fs(即分布式文件系统DistributedFileSystem),然后fs调用create()创建文件。

步骤2:DistributedFileSystem(fs)通过RPC调用NameNode在命名空间中创建一个新文件,此时该文件还没有相应的数据块。namenode会检查此文件是否已存在及client是否有权限新建文件,如果检查不通过,则创建失败并向client抛出IOException异常,否则namenode就会创建一条记录。

步骤3:然后DistributedFileSystem(fs)向client返回一个FSDataOutputStream(封装了DFSOutputStream)对象来写数据。在client写数据时,DFSOutputStream将它分成一个个的数据包并写入内部队列,称作数据队列(data queue)。

步骤4:DFSOutputStream会请求namenode在合适的datanodes(默认3个)上分配blocks来存储数据队列。3个datanodes形成一个管线DataStreamer将数据包流式的传输到管线中第一个datanode,第一个datanode存储数据包并发送到第二个datanode, 第二个datanode存储数据包并发送到第三个datanode。

步骤5:DFSOutputStream也维护了一个确认队列(ack queue),当收到管道中所有datanodes的确认信息后对应数据包才会从确认队列中删除。

步骤6:Client完成数据的写入后便对FSDataOutputStream调用close()方法。

步骤7:该操作将所有剩余的数据包写入管线,并等待确认,最后向namenode报告写完成。

 public void testUpload() throws IllegalArgumentException, IOException{
        
        // 构建一个输入流,将本机需要上传的文件写入到内存中
        FileInputStream in = new FileInputStream("D:\\settings.xml");
        
        // 构建一个输出流,将客户端内存的数据写入到HDFS文件系统指定的路径中
        FSDataOutputStream out = fileSystem.create(new Path("/input/sting.xml"), true);
        
        /*
         *    参数说明:
         *     in 
         *         代表输入流,读取HDFS文件系统的文件到本机内存中
         *     out
         *         代表输出流,将本机内存中的文件写入到本地磁盘中
         *     4096
         *         缓冲区大小
         *     true    
         *         自动关闭流,如果不使用自动关闭的话需要手动关闭输入输出流
         *         
         *         手动关闭输入输出流:
         *             IOUtils.closeStream(in);
         *            IOUtils.closeStream(out);
         */
        IOUtils.copyBytes(in, out, 4096, true);
        
    }

HDFS java API

  1. 导入hadoop jar包。新建一个folder类型的lib文件夹。将运行所需的jar包放入lib文件夹中。运行hdfs所需的hadoop依赖的jar包在解压缩包之后的share文件夹下的hadoop文件夹下的common文件夹下的lib文件夹下所有的jar包,以及common文件夹下的hadoop-common-2.8.1.jar和share文件夹下的hadoop文件夹下的hdfs文件夹下的hadoop-hdfs-client-2.8.1.jar及hdfs文件夹下的lib文件夹下的所有jar包。在这里插入图片描述
  2. 将以上所有存放在lib文件夹下的jar包全部,add to build path。在这里插入图片描述
  3. 上传文件到hdfs
//new Configuration 会自动从classpath加载core-site.xml,core-default.xml,hdfs-default.xml等配置文件
Configuration  conf = new Configuration();
//指定上传文件时的副本数
conf.set("dfs.replication","2");
//指定上传文件时文件块的大小
conf.set("dfs.blocksize","64m"); //以上两个配置文件也可以不指定,若不指定,则自动加载默认的配置环境
//构造一个访问hdfs的对象,参数1:指定hdfs的uri;参数2:客户端特别指定的参数;参数3:客户端的身份
FileSystem fs = FileSystem.get("new URI("hdfs的namenode所在的uri")",conf,"root");
//上传一个文件到hdfs上,参数1:本地的path路径;参数2:目标路径即hdfs的路径
fs.copyFromLocalFile("new path("本地路径")",new path("hdfs路径));
//关闭系统
fs.close();
  1. 从hdfs下载文件
//省略和以上代码相同的部分
//下载文件
fs.copyToLocalFile(new path("目标文件名所在路径");new path("要下载的hdfs的路径"));
  1. 在hdfs内部移动文件
fs.rename("new path("源路径")";new path("目标路径"));
  1. 在hdfs中创建文件夹
fs.mkdirs("new path("路径")");
  1. 删除hdfs中的文件
fs.delete("new path("路径")",true);

版权声明:本文为博主原创文章,转载请附上博文链接!
[原文]: https://blog.csdn.net/leen0304/article/details/77852178

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值