Hadoop-HDFS之Api编程

264 阅读2分钟

一、开发环境搭建

这里使用Maven项目构建开发环境,相关配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.imooc.bigdata</groupId>
    <artifactId>hadoop-train</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--版本管理-->
    <properties>
        <hadoop.version>2.6.0-cdh5.15.1</hadoop.version>
    </properties>
    <repositories>
    <!-- cloudera 的仓库 -->
      <repository>
        <id>cloudera</id>
        <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
      </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
    </dependencies>
</project>

二、编写相关API

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.net.URI;

/**
 * 使用java API操作HDFS文件系统
 */
public class HDFSApp {
    public static final String HDFS_PATH = "hdfs://121.**.***.81:8020/";
    FileSystem fileSystem = null;
    Configuration conf = null;

    @Before //测试前的相关代码
    public void setUp() throws Exception {
        conf = new Configuration();
        /**
         * 构造一个访问指定HDFS系统的客户端对象
         * 第一个参数:HDFS的URI
         * 第二个参数:客户端的配置参数
         * 第三个参数:客户端的身份,说白了就是用户名
         */
        conf.set("dfs.client.use.datanode.hostname", "true");
        fileSystem = FileSystem.get(new URI(HDFS_PATH), conf,"root");
    }

    /**
     * 创建HDFS文件夹
     */
    @Test
    public void test() throws Exception {
        Path path = new Path("/hdfsapi/test");
        boolean res = fileSystem.mkdirs(path);
        System.out.println(res);
    }

    /**
     * 查看HDFS内容
     */
    @Test
    public void test1() throws Exception{
        FSDataInputStream in = fileSystem.open(new Path("/test.txt"));
        IOUtils.copyBytes(in,System.out,1024);
    }
    @After
    public void tearDown(){
        conf = null;
        fileSystem = null;
    }
}

这里需要注意的是hadoop部署在阿里云上,在测试open方法读取文件的时候出现了错误,但是创建文件及文件夹都没有错误,读取文件的具体错误如下:

org.apache.hadoop.hdfs.BlockMissingException: Could not obtain block: BP-613411839-  
172.26.224.239-1606657223979:blk_1073741829_1005 file=/test.txt

原因:

查阅资料发现 HDFS 中的文件夹和文件名都是存放在 NameNode 上,操作不需要和 DataNode 通信,因此可以正常创建文件夹和创建文件说明本地和远程 NameNode 通信没有问题。

文件夹和文件名都是存放在 NameNode 上的,我本地可以通过公网访问 NameNode,所以创建文件夹和文件都可以,但是当我写数据的时候,NameNode 和DataNode 是通过内网通信的,NameNode 会返回给我 DataNode 的内网 IP,我本地就访问不了了。

还有一种可能,云服务器没有开放 DataNode 用于数据传输服务端口 默认是 50010。

解决方案:
1、添加一句配置,使 NameNode 返回 DataNode 的主机名而不是 IP:

configuration.set("dfs.client.use.datanode.hostname", "true");

另外也可以配置 hdfs-site.xml 文件内容如下:

<property>
    <name>dfs.client.use.datanode.hostname</name>
    <value>true</value>
</property>

2、本地可以拿到了 DataNode 的主机名,要访问还需要配置本地 Hosts 映射。

121.89.195.**  ch**j***
windows 下 hosts 文件地址:C:\Windows\System32\drivers\etc\hosts

3、云服务器打开 50010 端口