关于HDFS

目录

一、HDFS概述

二、HDFS架构与工作机制

官方文档Apache Hadoop 3.3.4 – HDFS Architecture

组成部分(基于master/slave策略)

NameNode和SecondaryNameNode的工作机制

DataNode的工作机制

HDFS的写流程

HDFS的读流程

网络拓扑-节点距离计算 & 机架感知(副本位置的选择)

数据完整性

扫描二维码关注公众号,回复: 14680067 查看本文章

三、HDFS的Shell操作

四、Hdfs的API操作


一、HDFS概述

  1. HDFS:Hadoop Distributed File System;一种分布式文件管理系统,通过目录树定位文件。
  2. 使用场景:一次写入,多次读出,且不支持文件的修改。适用于数据分析,不适用于网盘应用;
  3. 优点:
    1. 高容错:多个副本,其中一个副本丢失,可以自动恢复;
    2. 适合处理大数据:数据规模大 & 文件规模大  & 可以构建于廉价机器上。
  4. 缺点:
    1. 不适合低延时数据访问;
    2. 无法高效的对大量小文件进行存储;
    3. 不支持并发写同一个文件,仅支持追加,不支持文件的随机修改。

二、HDFS架构与工作机制

  1. 官方文档
    Apache Hadoop 3.3.4 – HDFS Architecture

  2. 组成部分(基于master/slave策略)

    1. NameNode(master):即Master,管理HDFS名称空间、副本策略、Block映射信息、处理客户端读写请求。
    2. DataNode(slave):即Slaver,存储实际的数据块,执行NN下达的读写操作。
    3. Secondary NameNode(SNN):不是NN的热备份(热备份是指在程序还在运行的时候对数据进行备份)SNN对于NN的作用不同于平常的热备份的概念,SNN包含Fsimage和Edits,会定期合并Fsimage和Edits并推送给NN。紧急情况下可辅助恢复NameNode。
    4. Client:客户端,交互与访问。
    5. Block(hdfs文件块)
      1. Hadoop1.x中是64M,在Hadoop2.x-3.x中是128M;
      2. 块的大小既不能太大,也不能太小;(块大小的设置取决于磁盘传输的速率);
  3. NameNode和SecondaryNameNode的工作机制

    1. NameNode启动
      1. 第一次启动NN,需要创建命名空间镜像文件(fsimage)和编辑日志文件(edits)(如果NN不是第一次启动,直接加载fsimage文件和edits文件到内存);
      2. NN记录操作日志,滚动日志;
      3. NN在内存中对元数据进行修改操作。
    2. SecondaryNameNode工作过程
      1. SNN询问NN是否需要CheckPoint(是否需要合并fsimage和edits),一般记录条数超过100w条或者时间超过3600s,Snn提醒NN进行CheckPoint。
      2. Secondary NameNode请求执行CheckPoint。
      3. NameNode滚动正在写的Edits日志(edit_inprogress滚动生成edit文件)。将滚动前的编辑日志和镜像文件拷贝到SNN,SNN加载编辑日志和镜像文件到内存进行合并,生成新的镜像文件fsimage.chkpoint。
      4. 拷贝fsimage.chkpoint到NameNode,NameNode将fsimage.chkpoint重新命名成fsimage。
  4. DataNode的工作机制

    1. 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据(包括数据块的长度,块数据的校验和,以及时间戳)。
    2. DataNode启动后向NameNode注册,通过后,周期性(6小时)地向NameNode上报所有的块信息。
    3. 心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
  5. HDFS的写流程

    1. 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查集群上目标文件是否已存在,父目录是否存在。
    2. NameNode返回是否可以上传。
    3. 客户端请求询问第一个 Block上传到哪几个DataNode服务器上。
    4. NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
    5. 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成(Pipe管道机制)。
    6. dn1、dn2、dn3逐级应答客户端。
    7. 客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet,就会将其放入一个应答队列等待应答。完成一个Block后DN会向NN发送heartbeat说明已经完成一个block传输。
    8. 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
    9. 传输完成后通知NameBode通知Client完成传输。
  6. HDFS的读流程

    1. 客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
    2. 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
    3. DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
    4. 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
  7. 网络拓扑-节点距离计算 & 机架感知(副本位置的选择)

    1. 距离如图所示
    2. 网络拓扑(将整个网络视为一个节点,各个大数据集群则是这整个网络节点的子节点,各个机架则分别是每个集群的子节点,每个实际节点又是机架节点的子节点。依次画出网络拓扑图并计算距离)
    3. 副本位置
      1. 第一个副本位于客户端所处的节点上,如果客户端不属于集群节点,则随机选择一个;
      2. 第二、三个副本位于另一个机架上的两个不同节点之上。
  8. 数据完整性

    1. DataNode读取Block的时候,它会计算CheckSum,计算Block是否已经损坏;
    2. DataNode在文件创建后周期验证CheckSum;

三、HDFS的Shell操作

  1. hadoop fs 某指令  与  hdfs dfs 某指令 是一样的
  2. Hadoop hdfs的启动与关闭(以下是自定义脚本开启关闭hdfs)
    #!/bin/bash
    
    if [ $# -lt 1 ]
    then
        echo "No Args Input..."
        exit ;
    fi
    
    case $1 in
    "start")
            echo " =================== 启动 hadoop集群 ==================="
    
            echo " --------------- 启动 hdfs ---------------"
            ssh hadoop101 "/opt/module/hadoop-3.1.3/sbin/start-dfs.sh"
            echo " --------------- 启动 yarn ---------------"
            ssh hadoop102 "/opt/module/hadoop-3.1.3/sbin/start-yarn.sh"
            echo " --------------- 启动 historyserver ---------------"
            ssh hadoop101 "/opt/module/hadoop-3.1.3/bin/mapred --daemon start historyserver"
    ;;
    "stop")
            echo " =================== 关闭 hadoop集群 ==================="
    
            echo " --------------- 关闭 historyserver ---------------"
            ssh hadoop101 "/opt/module/hadoop-3.1.3/bin/mapred --daemon stop historyserver"
            echo " --------------- 关闭 yarn ---------------"
            ssh hadoop102 "/opt/module/hadoop-3.1.3/sbin/stop-yarn.sh"
            echo " --------------- 关闭 hdfs ---------------"
            ssh hadoop101 "/opt/module/hadoop-3.1.3/sbin/stop-dfs.sh"
    ;;
    *)
        echo "Input Args Error..."
    ;;
    esac
  3. hdfs文件的上传下载
    * 格式:hadoop fs -指令 [参数列表]
    上传:
    1、本地"剪切"上传:hadoop fs  -moveFromLocal  ./localfile.txt  /hdfs_dir
    2、本地拷贝上传:
        hadoop fs  -copyFromLocal  ./localfile.txt  /hdfs_dir
        hadoop fs  -put ./localfile.txt  /hdfs_dir (工作环境更倾向于用put)
    3、追加(hdfs文件已存在)
        hadoop fs  -appendToFile  localfile.txt  /hdfs_dir/hdfsfile.txt
    =======================================================================================
    下载:
    1、hdfs拷贝至本地:
        hadoop fs -copyToLocal /hdfs_dir/hdfsfile.txt ./
        hadoop fs -get /hdfs_dir/hdfsfile.txt ./ (生产环境更倾向于用get)
  4. 其他Hadoop shell指令(略)

四、Hdfs的API操作

  1. 在Windows下配置Hadoop的运行环境
    1. 添加Hadoop的Windows依赖文件夹至Windows下一个纯英文路径,配置HADOOP_HOMEPath环境变量(Hadoop的Windows依赖官网没有直接提供,需要自行下载,双击winutils.exe可以验证环境变量是否正常,报错可能是因为缺少微软运行库)
    2. 新建Idea Maven项目(配置阿里云maven镜像可以使下载的速度更快)
      1. 导入相应的依赖坐标(修改pom.xml - import changes)
        <dependencies>
            <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-client</artifactId>
                <version>3.1.3</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.30</version>
            </dependency>
        </dependencies>
      1. 日志添加(在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入)
        log4j.rootLogger=INFO, stdout
        log4j.appender.stdout=org.apache.log4j.ConsoleAppender
        log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
        log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
        log4j.appender.logfile=org.apache.log4j.FileAppender
        log4j.appender.logfile.File=target/spring.log
        log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
        log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
    3. API编写代码(*重新理解API这个宽泛的概念)
      1. Java代码的大致逻辑:获取客户端对象 → 执行操作 → 关闭资源(Java标签的用法也可以了解一下,挺好用的)
        package cn.hadoop.hdfs;
        
        import org.apache.hadoop.conf.Configuration;
        import org.apache.hadoop.fs.*;
        import org.junit.After;
        import org.junit.Before;
        import org.junit.Test;
        
        import java.io.IOException;
        import java.net.URI;
        import java.net.URISyntaxException;
        import java.sql.Array;
        import java.util.Arrays;
        
        
        public class HdfsClient {
            private URI uri;
            private Configuration conf;
            private String user = "hadoop";       //文件系统用于进行操作的用户名,这个用户名决定了操作的权限。
            private FileSystem fs;
        
            @Before
            public void init() throws URISyntaxException, IOException, InterruptedException {  //
                uri = new URI("hdfs://hadoop101:8020");    // 统一资源定位符:8020是NameNode内部通信端口
                conf = new Configuration();                    //配置对象默认为空
                fs = FileSystem.get(uri, conf, user);          //使用get()获取FileSystem实例
            }
        
            @After
            public void close() throws IOException {
                fs.close();                                   // 关闭FileSystem实例
            }
        
            @Test
            //创建目录
            public void testmkdir() throws URISyntaxException, IOException, InterruptedException {
                fs.mkdirs(new Path("/目录名"));       //mkdirs()方法:创建一个新的目录
            }
        
            @Test
            //测试各个部分配置文件的优先级
            /*
             * 	代码内部的配置优先级 > 项目资源目录下的配置文件的优先级 > Linux上的配置文件 > 默认配置
             * */
            public void testPut() throws IOException, InterruptedException {
                conf.set("dfs.replication", "2");     //node level参数,指定每个block在集群上有几个备份(因为这个参数在hdfs-site.xml也有配置,所以可以用来比较这些参数之间的优先级)
                fs = FileSystem.get(uri, conf, user);
                fs.copyFromLocalFile(false, true, new Path("D:\\Edelweiss.txt"), new Path("hdfs://hadoop101/目录名称")); //hdfs文件路径开始的一部分是主机名
            }
        
            @Test
            //文件 下载
            public void testGet() throws IOException {
                fs.copyToLocalFile(false, new Path("/集群上的路径"), new Path("C:\\Users\\用户名\\Desktop"), false);
            }
        
            //删除delete、移动rename、更名rename等操作 略
        
            @Test
            //列出目录详细信息(也可以用于判断“路径类型”——文件或者目录)
            public void fileDetail() throws IOException {
                final RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);  //迭代器
                while (listFiles.hasNext()) {
                    LocatedFileStatus fileStatus = listFiles.next();   //文件状态
                    System.out.println("---------------" + fileStatus.getPath() + "---------------");
                    System.out.println(fileStatus.getPermission());
                    System.out.println(fileStatus.getBlockLocations());
                    final BlockLocation[] blockLocations = fileStatus.getBlockLocations();
                    System.out.println(Arrays.toString(blockLocations));
        
                }
        
            }
        
        
            @Test
            public void Test(){
                System.out.println("okk"+"\n");
                System.out.println("ookk"+"\\N");
            }
        }
        
        
      2. 关于代码内部配置的优先级 > 项目资源目录下的配置文件的优先级 > Linux上的配置文件 > 默认配置。
    4. 遇见的报错:
      1. 不支持发行版本5:https://www.cnblogs.com/KennyWang0314/p/12268953.html 按图设置成与自己本地jdk匹配的版本就好了。之前maven是默认Java5,而我的本地是jdk11,无法生成Java5二进制文件。

猜你喜欢

转载自blog.csdn.net/Jinandawang/article/details/128953718