分布式文件系统HDFS

分布式文件系统HDFS

HDFS的基础架构

1、NameNode是一个中心服务器,负责管理文件系统的名字空间以及客户端对文件的访问

2、文件操作,namenode是负责文件元数据的数据,datanode负责处理文件的读写请求,跟文件相关的数据流不经过namenode,值询问数据和哪个datanode有联系

3、副本的存放位置由namenode来控制,根据全局情况来决定,读取文件时namenode尽量让用户先读取最近的副本

4、Namenode全权管理数据库的赋值,他周期性的从集群中每个DataNode接收心跳信号和状态报告,心跳信号意味着节点工作正常,块状态报告包含了Datanode上所有的数据列表

文件副本机制和block块存储

所有的文件都是以block块的方式存放在HDFS的文件系统中,在Hadoop1.x中默认的块大小为64M,Hadoop2.x中,默认的块大小为128M,block的块大小可以通过hdfs-site.xml当中的配置dfs.block.size进行指定

HDFS的元数据信息FSimage、edits以及secondaryNameNode的作用

edits:客户端对hdfs进行写文件时会首先被记录在edits中,edits修改时原数据也会更新,在hdfs更新时,edits先更新后客户端才会看到最新的信息。

fsimage:namenode中的元数据的镜像,一般称为检查点

随着edits内容的增多,就需要在一定的时间点和fsimage合并

fsimage的文件信息查看

官方文档:http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.14.0/hadoop-project-dist/hadoop-hdfs/HdfsEditsViewer.html

命令: hdfs oiv

edits的文件信息查看

官方文档:http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.14.0/hadoop-project-dist/hadoop-hdfs/HdfsEditsViewer.html

命令: hdfs oev

secondarynamenode管理FSimage和edits

1、secondarynamenode先通知namenode需要合并fsimage和edits,让namenode暂时将新的操作放入一个新的文件(edits.new)中

2、secondarynamenode从namenode中通过http get 获得edits和fsimage

3、secondarynamenode将fsimage载入内存然后合并edits,合并后生成新的fsimage

4、secondarynamenode通过http post的方式将fsimage发送给namenode

5、namenode从secondarynamenode获得了fsimage之后,替换掉原来的fsimage,同时把edits.new变成edits

合并的条件:

fs.checkpoint.period: 默认是一个小时(3600s)
fs.checkpoint.size: edits达到一定大小时也会触发合并(默认64MB)

HDFS的文件写入过程

1、客户端请求namenode,告诉namenode需要上传的文件

2、namenode接收到客户端的请求,校验客户端是否能上传该文件

3、客户端继续请求namenode第一个block块需要上传到哪个datanode上

4、namenode按照机架感知原理找到让客户端上传的机器返回(机架感知:假如文件的副本有三个,namenode会找找三台机器,第一台机器,里客户端最近的一台机器,第二个,最近的那台机器的相同路由下找一台机器,第三个,不同的路由下找一台机器)

5、客户端与datanode进行通信,建立数据通信管道pipeline,客户端的数据以package的形式发送到第一台机器,然后再发送到其他机器上

6、第一个block块写完后,datanode给客户端一个ack确认机制,客户端重复操作,上传剩下的块

HDFS的文件读取过程

1、客户端发送请求,请求nannode需要读取数据

2、namenode接收客户端请求,校验客户端是否能读取数据,如果可以读取,发送消息告诉客户端可以读取,并查找元数据信息告诉客户端block块的存储位置

3、客户端获取到block的地址后,与datanod通信,通过管道pipeline进行数据传输,直到读取完全不的块,再客户端再拼接成完整的文件

注意:如果客户端在读取block时,中断了,客户端会重新发送请求下载这个块,原先的下载了一部分的内容会删除

HDFS的API操作

由于cdh版本的所有的软件涉及版权的问题,所以并没有将所有的jar包托管到maven仓库当中去,而是托管在了CDH自己的服务器上面,所以我们默认去maven的仓库下载不到,需要自己手动的添加repository去CDH仓库进行下载,以下两个地址是官方文档说明

https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo.html

https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo_514x.html

可能jar包下载 不了,可以修改maven的配置文件

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*,!cloudera</mirrorOf>
    <name>Nexus aliyun</name>                     
    <url>
      http://maven.aliyun.com/nexus/content/groups/public
    </url>
</mirror>

pom.xml

<repositories>
    <repository>
        <id>cloudera</id>
        <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.6.0-mr1-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>RELEASE</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <!--    <verbal>true</verbal>-->
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <minimizeJar>true</minimizeJar>
                    </configuration>
                </execution>
            </executions>
        </plugin>
      <!--  <plugin>
            <artifactId>maven-assembly-plugin </artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>cn.itcast.hadoop.db.DBToHdfs2</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>-->
    </plugins>
</build>

使用文件系统方式访问数据

获取FileSystem的四种方式

/**
     * 获取文件系统(一)
     * @throws IOException
     */
    @Test
    public void getFileSystem() throws IOException {
        Configuration configuration = new Configuration();
        configuration.set("fs.defaultFS","hdfs://node01:8020");
        FileSystem fileSystem = FileSystem.get(configuration);
        System.out.println(fileSystem.toString());
    }

    /**
     * h获取文件系统(二)
     * @throws URISyntaxException
     * @throws IOException
     */
    @Test
    public void getFileSystem2() throws URISyntaxException, IOException {
        Configuration conf = new Configuration();
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), conf);
        System.out.println(fileSystem.toString());
    }

    /**
     * 获取文件系统(三)
     * @throws IOException
     */
    @Test
    public void getFileSystem3() throws IOException {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://node01:8020");
        FileSystem fileSystem = FileSystem.newInstance(conf);
        System.out.println(fileSystem.toString());
    }

    /**
     * 获取文件系统(四)
     * @throws URISyntaxException
     * @throws IOException
     */
    @Test
    public void getFileSystem4() throws URISyntaxException, IOException {
        Configuration conf = new Configuration();
        FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), conf);
        System.out.println(fileSystem.toString());
    }

官方API操作,获取所有文件的路径

 /**
     * 官方API操作API:获取所有文件的路径
     * @throws URISyntaxException
     * @throws IOException
     */
    @Test
    public void listFiles() throws URISyntaxException, IOException {
        //先获取文件系统
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
        //通过官方API操作:得到所有的文件  第一个参数是路径,第二个参数是是否递归遍历
        RemoteIterator<LocatedFileStatus> listFiles = fileSystem.listFiles(new Path("/"), true);
        while (listFiles.hasNext()){
            LocatedFileStatus next = listFiles.next();
            System.out.println(next.getPath().toString());
        }
        fileSystem.close();
    }

下载文件到本地

 /**
     * 下载文件到本地
     */
    @Test
    public void downLoadFile() throws URISyntaxException, IOException {
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
        //流操作
        FSDataInputStream open = fileSystem.open(new Path("/test/install.log"));
        FileOutputStream outputStream = new FileOutputStream("d:/install.log");
        IOUtils.copy(open,outputStream);
        //关闭流
        IOUtils.closeQuietly(open);
        IOUtils.closeQuietly(outputStream);
        //关闭文件系统
        fileSystem.close();
    }

hdfs上创建文件

/**
     * hdfs上创建文件
     */
    @Test
    public void mkdirs() throws URISyntaxException, IOException {
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
        boolean mkdirs = fileSystem.mkdirs(new Path("/test/abc"));
        fileSystem.close();
    }

hdfs文件上传

/**
     * hdfs文件上传
     */
    @Test
    public void putData() throws URISyntaxException, IOException {
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
        fileSystem.copyFromLocalFile(new Path("file:///d:/install.log"),new Path("/test/abc"));
        fileSystem.close();
    }

HDFS的权限问题以及伪造用户

在hdfs-site.xml配置中有dfs.permissions这个配置,默认是false

更改为true之后,在进行文件操作是需要伪装用户

       <property>
                <name>dfs.permissions</name>
                <value>true</value>
       </property>

下载文件

@Test
public void getConfig()throws  Exception{
    //在获取文件系统时需要伪装用户,获得操作权限
    FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(),"root");
    fileSystem.copyToLocalFile(new Path("/config/core-site.xml"),new Path("file:///c:/core-site.xml"));
    fileSystem.close();
}

HDFS的小文件合并

Hadoop擅长存储大文件,因为大文件的元数据信息较少,当集群中有较多的小文件时,每个文件都有一份元数据信息,大大增加了集群管理元数据的内存压力,很有必要将小文件合并成大文件来一起处理

可以通过命令将很多的hdfs文件合并成一个大文件下载到本地

hdfs dfs -getmerge /config/*.xml  ./hello.xml

通过代码实现在上传时将小文件合并成大文件上传

 /**
     * 将上传文件合并成大文件上传
     */
    @Test
    public void mergeFile() throws URISyntaxException, IOException, InterruptedException {
        //获取文件系统和创建写入的文件
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(), "root");
        FSDataOutputStream fsDataOutputStream = fileSystem.create(new Path("/test.xml"));
        //获取本地的文件系统
        LocalFileSystem local = FileSystem.getLocal(new Configuration());
        //获取文件列表集合
        FileStatus[] listStatus = local.listStatus(new Path("D:\\test"));
        for (FileStatus status : listStatus) {
            //循环写入目标文件
            FSDataInputStream open = local.open(status.getPath());
            IOUtils.copy(open,fsDataOutputStream);
            IOUtils.closeQuietly(open);
        }
        IOUtils.closeQuietly(fsDataOutputStream);
        local.close();
        fileSystem.close();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值