【问题标题】:Reading remote HDFS file with Java使用 Java 读取远程 HDFS 文件
【发布时间】:2014-04-26 05:53:56
【问题描述】:

我在安装简单的 Hadoop 时遇到了一些麻烦。我已经下载了 hadoop 2.4.0 并安装在单个 CentOS Linux 节点(虚拟机)上。如 apache 站点 (http://hadoop.apache.org/docs/r2.4.0/hadoop-project-dist/hadoop-common/SingleCluster.html) 所述,我已经为具有伪分布的单个节点配置了 hadoop。它从日志中没有问题开始,我可以使用命令行中的“hadoop fs”命令读取和写入文件。

我正在尝试使用 Java API 从远程计算机上的 HDFS 读取文件。机器可以连接并列出目录内容。也可以通过代码判断文件是否存在:

Path p=new Path("hdfs://test.server:9000/usr/test/test_file.txt");
FileSystem fs = FileSystem.get(new Configuration());
System.out.println(p.getName() + " exists: " + fs.exists(p));

系统打印“true”表示它存在。但是,当我尝试使用以下命令读取文件时:

BufferedReader br = null;
try {
    Path p=new Path("hdfs://test.server:9000/usr/test/test_file.txt");
    FileSystem fs = FileSystem.get(CONFIG);
    System.out.println(p.getName() + " exists: " + fs.exists(p));

    br=new BufferedReader(new InputStreamReader(fs.open(p)));
    String line = br.readLine();

    while (line != null) {
        System.out.println(line);
        line=br.readLine();
    }
}
finally {
    if(br != null) br.close();
}

这段代码抛出异常:

线程“main”org.apache.hadoop.hdfs.BlockMissingException 中的异常:无法获取块:BP-13917963-127.0.0.1-1398476189167:blk_1073741831_1007 文件=/usr/test/test_file。 txt

谷歌搜索提供了一些可能的提示,但都已检查。数据节点已连接、处于活动状态并且有足够的空间。 hdfs dfsadmin –report 的管理员报告显示:

配置容量:52844687360 (49.22 GB)
当前容量:48507940864 (45.18 GB)
剩余 DFS:48507887616 (45.18 GB)
使用的 DFS:53248 (52 KB)
DFS 已用百分比:0.00%
复制块下:0
具有损坏副本的块:0
缺少块:0

可用数据节点:1 个(总共 1 个,0 个死节点)

实时数据节点:
名称:127.0.0.1:50010 (test.server)
主机名:test.server
退役状态:正常
配置容量:52844687360 (49.22 GB)
使用的 DFS:53248 (52 KB)
未使用 DFS:4336746496 (4.04 GB)
剩余 DFS:48507887616 (45.18 GB)
DFS 已用百分比:0.00%
DFS 剩余百分比:91.79%
配置的缓存容量:0 (0 B)
使用的缓存:0 (0 B)
缓存剩余:0 (0 B)
缓存已用百分比:100.00%
缓存剩余百分比:0.00%
最后联系时间:2014 年 4 月 25 日星期五 22:16:56 PDT

客户端 jars 是直接从 hadoop 安装复制的,因此没有版本不匹配。我可以使用我的 Java 类浏览文件系统并读取文件属性。我只是无法在没有异常的情况下读取文件内容。如果我尝试用代码编写文件:

FileSystem fs = null;
BufferedWriter br = null;

System.setProperty("HADOOP_USER_NAME", "root");

try {
    fs = FileSystem.get(new Configuraion());

    //Path p = new Path(dir, file);
    Path p = new Path("hdfs://test.server:9000/usr/test/test.txt");
    br = new BufferedWriter(new OutputStreamWriter(fs.create(p,true)));
    br.write("Hello World");
}
finally {
    if(br != null) br.close();
    if(fs != null) fs.close();
}

这会创建文件但不写入任何字节并引发异常:

线程 "main" org.apache.hadoop.ipc.RemoteException(java.io.IOException) 中的异常:文件 /usr/test/test.txt 只能复制到 0 个节点而不是minReplication (=1)。有 1 个数据节点正在运行,并且在此操作中排除了 1 个节点。

谷歌搜索表明可能存在空间问题,但从 dfsadmin 报告来看,似乎有足够的空间。这是一个普通的香草安装,我无法解决这个问题。

环境总结为:

服务器:

具有伪分布的 Hadoop 2.4.0 (http://hadoop.apache.org/docs/r2.4.0/hadoop-project-dist/hadoop-common/SingleCluster.html)

CentOS 6.5 虚拟机 64位服务器 Java 1.7.0_55

客户:

Windows 8(虚拟机) Java 1.7.0_51

非常感谢任何帮助。

【问题讨论】:

  • 检查所有 live 节点和命名节点的内部设置。因为它在你的问题中是 127.0.0.1
  • 或者你能把你的日志放在这里吗?

标签: java linux apache hadoop hdfs


【解决方案1】:

Hadoop 错误消息令人沮丧。他们通常不会说出他们的意思,并且与真正的问题无关。当客户端、namenode 和 datanode 无法正常通信时,我见过这样的问题。在您的情况下,我会选择以下两个问题之一:

  • 您的集群在 VM 中运行,其对客户端的虚拟化网络访问被阻止。
  • 您没有始终使用在客户端和主机之间进行相同解析的完全限定域名 (FQDN)。

主机名“test.server”非常可疑。检查以下所有内容:

  • test.server 是 FQDN 吗?
  • 这是在您的 conf 文件中随处使用的名称吗?
  • 客户端和所有主机能否正向和反向解析 “test.server”和它的IP地址并得到同样的东西?
  • 是否在任何地方都使用 IP 地址而不是 FQDN?
  • “localhost”是否在任何地方使用?

必须删除 FQDN、主机名、数字 IP 和本地主机使用中的任何不一致之处。永远不要将它们混入您的 conf 文件或客户端代码中。一致使用 FQDN 是首选。一致使用数字 IP 通常也可以。使用不合格的主机名、localhost 或 127.0.0.1 会导致问题。

【讨论】:

  • 补充一点,如果您的节点属于私有网络,并且您的客户端无法通过其私有 IP 访问数据节点,您可能会遇到同样的问题。在这种情况下,您需要从属文件指向公共 DNS 可解析主机名或数据节点公共 IP
  • 是的,但比这更糟。 Hadoop FileSystem 和 Path 类以及使用它们的文件 I/O 类通常具有嵌入式逻辑,用于检查文件系统 URI 和数据 URI 之间的一致性,如果您混合使用 IP/FQDN/主机名,无论您是否使用DNS 配置正确地解决了所有这些问题。
【解决方案2】:

我们需要确保配置有 fs.default.name 空间集,例如

configuration.set("fs.default.name","hdfs://ourHDFSNameNode:50000");

下面我放了一段示例代码:

 Configuration configuration = new Configuration();
 configuration.set("fs.default.name","hdfs://ourHDFSNameNode:50000");
 FileSystem fs = pt.getFileSystem(configuration);
 BufferedReader br = new BufferedReader(new InputStreamReader(fs.open(pt)));
 String line = null;
 line = br.readLine
 while (line != null) {
  try {
    line = br.readLine
    System.out.println(line);
  }
}

【讨论】:

    【解决方案3】:

    上面的答案指向了正确的方向。请允许我添加以下内容:

    1. Namenode 不直接读取或写入数据。
    2. 客户端(使用直接访问 HDFS 的 Java 程序)与 Namenode 交互以更新 HDFS 命名空间并检索块位置以进行读/写。
    3. 客户端直接与 Datanode 交互以读取/写入数据。

    您能够列出目录内容,因为您的客户端代码可以访问hostname:9000。您正在执行上述第 2 项。
    为了能够读写,您的客户端代码需要访问 Datanode(编号 3)。 Datanode DFS 数据传输的默认端口是 50010。某些东西阻止了您的客户端与 hostname:50010 的通信。可能是防火墙或 SSH 隧道配置问题。
    我使用的是 Hadoop 2.7.2,所以可能你有不同的端口号设置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多