hdfs/libhdfs的使用

注:本文完全为作者本人操作过程的记录,文字十分杂乱无章,谨慎参考。

1. 失败的第一步

https://github.com/erikmuttersbach/libhdfs3

先把仓库拷贝下来

cd LIBHDFS3_HOME
mkdir build
cd build
../bootstrap

这个时候遇到了报错
Could NOT find LibXml2 (missing: LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
看来得装一下这个
参考 https://blog.csdn.net/shanzhizi/article/details/7726679

#sudo apt-get install libxml2

#sudo apt-get install libxml2-dev
这样就行了

又会报错
Could NOT find Protobuf (missing: Protobuf_INCLUDE_DIR)
奇怪我电脑上明明又protobuf的
参考
https://blog.csdn.net/sinat_28442665/article/details/115171421
直接装上

安装uuid

sudo apt-get install uuid-dev 

安装Kerberos,参考https://computing.help.inf.ed.ac.uk/kerberos-ubuntu#:~:text=Installing%20Kerberos%201%20In%20a%20terminal%20window%2C%20run,Installation%20should%20complete%20itself%20from%20this%20point%20on.

sudo apt-get update
sudo apt-get install krb5-user

2. 正确安装

放弃了,太抽象了,直接用下载Hadoop里面带的库

直接编译会报错

g++ -o test hdfs_test.cpp -I${HADOOP_HOME}/include -L${HADOOP_HOME}/lib/native -lhdfs -Wl,-rpath=/usr/lib/jvm/jdk-18.0.1.1/lib

Java的库在
/usr/lib/jvm/jdk-18.0.1.1/lib
hdfs的库也下载下来了,怎么用呢

只能说这个使用的过程非常抽象,在网上也不能很好的找到教程

现在我们从hdfs官网找到这么一个代码,拷贝下来

#include "hdfs.h" 

#include <cstdio>
#include <cstring>
#include <string>

int main(int argc, char **argv) {

    hdfsFS fs = hdfsConnect("default", 0);
    const char* writePath = "/tmp/testfile.txt";
    hdfsFile writeFile = hdfsOpenFile(fs, writePath, O_WRONLY|O_CREAT, 0, 0, 0);
    if(!writeFile) {
          fprintf(stderr, "Failed to open %s for writing!\n", writePath);
          exit(-1);
    }
    char* buffer = "Hello, World!";
    tSize num_written_bytes = hdfsWrite(fs, writeFile, (void*)buffer, strlen(buffer)+1);
    if (hdfsFlush(fs, writeFile)) {
           fprintf(stderr, "Failed to 'flush' %s\n", writePath); 
          exit(-1);
    }
   hdfsCloseFile(fs, writeFile);
}

想编译这个代码

g++ -o test hdfs_test.cpp -I${HADOOP_HOME}/include -L${HADOOP_HOME}/lib/native -lhdfs

会报错

/usr/bin/ld: warning: libjvm.so, needed by /usr/local/hadoop/lib/native/libhdfs.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: /usr/local/hadoop/lib/native/libhdfs.so: undefined reference to `JNI_GetCreatedJavaVMs@SUNWprivate_1.1'
/usr/bin/ld: /usr/local/hadoop/lib/native/libhdfs.so: undefined reference to `JNI_CreateJavaVM@SUNWprivate_1.1'
collect2: error: ld returned 1 exit status

注意libjvm.so, not found,但是我们安装过Java,这个文件是在的
在这里插入图片描述
需要引导g++找到这里,根据网上的信息https://anhqle.github.io/rstudio-cannot-load-rjava-in-ubuntu/
设置

export LD_LIBRARY_PATH=/usr/lib/jvm/jdk-18.0.1.1/lib/server

再编译,就可以成功了。但是执行的时候会报错。

./test: error while loading shared libraries: libhdfs.so.0.0.0: cannot open shared object file: No such file or directory

观察报错,提醒我们,找不到hdfs这个动态库
找到这篇文章。https://blog.csdn.net/qq_41263444/article/details/119909510
也就是说我们库是存在的,只是又没有找到的,用同样的方法指示

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/hadoop/lib/native
g++ -o test hdfs_test.cpp -I${HADOOP_HOME}/include -L${HADOOP_HOME}/lib/native -lhdfs

再执行就会报新的错误。

Environment variable CLASSPATH not set!
getJNIEnv: getGlobalJNIEnv failed
Failed to open /tmp/testfile.txt for writing!

到这里已经进步很多了。可以看到hdfs的函数是确实有在执行了。
那么我们就有两个问题,classpath env的设置和getjnienv

解决方法见:https://blog.csdn.net/qq_25379821/article/details/85125727

➜  4th git:(dev)export HADOOP_PREFIX=$HADOOP_HOME                                   
➜  4th git:(dev)export CLASSPATH=$($HADOOP_PREFIX/bin/hadoop classpath --glob)
WARNING: HADOOP_PREFIX has been replaced by HADOOP_HOME. Using value of HADOOP_PREFIX.

再执行。得到结果,芜!完美
在这里插入图片描述

梳理

回头看,我们在执行这个代码的过程中做了哪些。

  1. 我们使用g++编译c++代码
  2. 我们链接了jvm和hdfs的动态库,这些库都是以普通的文件存在的,并不依托于我们本身执行的代码。这些库独立存在,我们做的只是在编译过程中指示出它们在哪
  3. 我们设置了若干个环境变量,供执行文件使用,包括HADOOP_HOME和CLASSPATH

综上我们使用bazel应该同样能方便的编译

顺便使用ctrl+shift+p,选择c++ configuration,告诉vscode到哪里找头文件,为了美观
在这里插入图片描述
在这里插入图片描述

那么摆在我们面前两个课题,如何用bazel编译libhdfs,如何与远端服务器交互

先把这些动态库全拷贝过来。

cc_library(
    name = "libhdfs",
    srcs = ["third-party/libhdfs/lib/libhdfs.so"],
    hdrs = ["third-party/libhdfs/include/hdfs.h"]
)

这样是能编译成的,但是会报错WARNING: Ignoring JAVA_HOME, because it must point to a JDK, not a JRE.
不确定会不会有影响

bazel build :libhdfs
cc_library(
    name = "hdfs",
    srcs = ["third-party/libhdfs/lib/libhdfs.so.0.0.0"],
    hdrs = ["third-party/libhdfs/include/hdfs.h"],
    includes = [
        "third-party/libhdfs/include/",
    ],
    copts = COPTS,
    linkstatic = True,
)

cc_binary(
    name = "hdfs_test",
    srcs = [
        "try_blade/4th/hdfs_test.cpp",
    ],
    copts = COPTS,
    deps = [
        ":hdfs",
    ],
    
    includes = [
        "node_module/src",
    ],
    linkstatic = True,
)

现在这玩意是可以编译执行的,但是这就不好办了,这玩意是需要动态库的,那我怎么分发呢。把hdfs改成静态库又不行,编译不过.
是不是得研究一下怎么用这玩意的.a文件来编译

Tue 11 Oct 2022 02:16:59 PM CST
先在每个服务器上装好java和Hadoop
会不会是这样,只要我设定好LD_LIBRARY_PATH的值,指向已经存在的so文件的路径
即使是依赖so的可执行文件也能分发

13 Oct
好奇怪,现在想想hdfs的问题。核心的端口设置在/usr/local/hadoop/etc/hadoopcore-site.xml

目前我们设置过两种情况

hdfs://172.20.188.154:9000
47.95.212.209:9000

分别对应私网IP和公网IP
两种设置都没法从本地链接到这台server。设置为私网IP的时候才能看到有在监听9000端口。从本地能ping到服务器。这是什么情况。
当设置为公网IP的时候会出问题,不能正常监听9000端口。跟ssh的配置有关系吗.

Fri 14 Oct 2022 03:17:33 PM CST
可能通过这个解决吗 https://blog.csdn.net/SartinL/article/details/105488843
防火墙关了也不行?
要把jdk版本也换成1.8吗

Mon Oct 17 08:48:43 PM CST 2022
突然发现一个问题。执行hdfs需要在client机器上能够不需要密码的登录服务器。如下所示,
在这里插入图片描述
但是这在我实际的客户端机器上却做不到
在这里插入图片描述
需要输入密码。这里可能是一个伏笔.

通过加入authorized keys解决了。但是仍然不能连接,很奇怪
参考https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html
可能是java版本导致的?试试能不能通过切换版本来解决,到1.8

Wed 19 Oct 2022 09:35:18 PM CST
根据这个教程在本地wsl下载了java1.8
https://www.linuxidc.com/Linux/2019-08/159870.htm

新的lib path

export LD_LIBRARY_PATH=/usr/lib/jvm/adoptopenjdk-8-hotspot-amd64/jre/lib/amd64/server

还是不行吗?问题在哪里

Thu 20 Oct 2022 04:34:07 PM CST
okok冷静下来,我们已经前进了一大步了
那么此前我们一直在尝试找各种各样的hdfs的设置问题,包括防火墙问题。可能的ssh链接问题,等等等等。最终看起来这与阿里云服务器的安全组控制有关。

回看我们做的一些尝试,我们在本地wsl将java版本切换到了1.8.
我们完全关闭了服务器的防火墙。
我们设置了免密登录,将id_rsa.pub的内容复制进了服务器的authorized_keys里面,使得ssh可以不使用密码就登录。

两个非常关键的参考是这两篇资料,它们无一例外都使用了阿里云服务器,我想这也是重要的一点。
https://blog.csdn.net/hrainning/article/details/100689858
https://blog.csdn.net/sandmswift/article/details/123971196
所以在此基础上我修改了xml配置如下:

<!-- core-site.xml -->
<configuration>
        <property>
                <name>fs.defaultFS</name>
                <value>hdfs://0.0.0.0:9000</value>
        </property>
</configuration>
<!-- hdfs-site.xml -->
<configuration>
<property>
    <name>dfs.replication</name>
    <value>1</value>
</property>

<property>
    <name>dfs.namenode.http-address</name>
    <value>0.0.0.0:50070</value>
</property>
<property>
        <name>dfs.webhdfs.enabled</name>
        <value>true</value>
</property>
</configuration>

设置了IP映射/etc/hosts
添加公网IP到主机名(但我不确定这是否有用)
修改了workers文件为公网IP

最关键的是看到了这句话{登录阿里云控制台,修改安全组,修改完毕后重启阿里云}。
在这里插入图片描述
这才使得事情真正有变化。

但是现在仍然不理想。我们看到3个现象。

  1. jps的结果没有显示datanode
➜  hadoop jps
66284 Jps
65389 SecondaryNameNode
65022 NameNode
  1. website无结果
    此站点的连接不安全47.95.212.209 发送了无效的响应。ERR_SSL_PROTOCOL_ERROR

  2. libhdfs沟通无效
    File /tmp/testfile.txt could only be written to 0 of the 1 minReplication nodes.
    There are 0 datanode(s) running and 0 node(s) are excluded in this operation.


从上面第三点来看,明显与第一点有关。可能可以通过修改配置文件解决?
之前查到过这个问题,报错格式一致。但是显然不是同一个问题
https://blog.csdn.net/renzhehongyi/article/details/77089743

喵的还是不行。又做了很多尝试。
关于datanode不存在,参考https://stackoverflow.com/questions/26545524/there-are-0-datanodes-running-and-no-nodes-are-excluded-in-this-operation
把/tmp下的文件全删了再重启就可以。

能看到remote视角能看到的remote是172.。*:9866,这是有问题的。因为这个是内网IP,本地是ping不到的
学到了/etc/hosts其实就是本地的dns映射
参考 https://www.jianshu.com/p/a3947b37b8ae
尝试修改配置让服务器返回hostname但是好像失败了。
而且应该返回的是公网IP啊?
也把9866这个IP加到安全组了,还有什么办法呢.

类目了,折腾了一周终于整明白了。
先看成果

➜  finalized hadoop fs -ls /tmp
Found 1 items
-rw-r--r--   3 knoe supergroup         14 2022-10-20 21:43 /tmp/testfile.txt
➜  finalized cd
➜  ~ ls
download  workspace
➜  ~ cd workspace
➜  workspace ls
➜  workspace hadoop fs -get /tmp/testfile.txt
➜  workspace ls
testfile.txt
➜  workspace cat testfile.txt
Hello, World!%
#include "hdfs.h" 

#include <cstdio>
#include <cstring>
#include <string>

int main(int argc, char **argv) {

//     hdfsFS fs = hdfsConnect("default", 0);
	// 这里是服务器的公网IP
    hdfsFS fs = hdfsConnect("hdfs://47.*.***.***", 9000);

    const char* writePath = "/tmp/testfile.txt";
    hdfsFile writeFile = hdfsOpenFile(fs, writePath, O_WRONLY|O_CREAT, 0, 0, 0);
    if(!writeFile) {
          fprintf(stderr, "Failed to open %s for writing!\n", writePath);
          exit(-1);
    }
    char* buffer = "Hello, World!";
    tSize num_written_bytes = hdfsWrite(fs, writeFile, (void*)buffer, strlen(buffer)+1);
    if (hdfsFlush(fs, writeFile)) {
           fprintf(stderr, "Failed to 'flush' %s\n", writePath); 
          exit(-1);
    }
   hdfsCloseFile(fs, writeFile);
}

注意在客户端和服务端两侧都要配置 服务器公网IP-hostname的映射
在这里插入图片描述
参考
https://www.jianshu.com/p/a3947b37b8ae

这件事的本质是,服务器的namenode和datanode之间是通信关系。服务器返回给client的总是datanode的IP,而这个IP是内网IP,使得client无法链接到这个datanode,并将其exclude。

这里面最关键的配置是

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

这使得服务器返回的是hostname?no
这使得namenode和datanode通过hostname沟通!但仍然返回一个datanode的IP给client。这个IP就要走dns的映射。
所以另一个关键是
我们注释掉/etc/hosts中的内网IP映射,否则hdfs在处理一个hostname时是的行为是不确定的【有多个IP与之对应】

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值