注:本文完全为作者本人操作过程的记录,文字十分杂乱无章,谨慎参考。
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.
再执行。得到结果,芜!完美
梳理
回头看,我们在执行这个代码的过程中做了哪些。
- 我们使用g++编译c++代码
- 我们链接了jvm和hdfs的动态库,这些库都是以普通的文件存在的,并不依托于我们本身执行的代码。这些库独立存在,我们做的只是在编译过程中指示出它们在哪
- 我们设置了若干个环境变量,供执行文件使用,包括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个现象。
- jps的结果没有显示datanode
➜ hadoop jps
66284 Jps
65389 SecondaryNameNode
65022 NameNode
-
website无结果
此站点的连接不安全47.95.212.209 发送了无效的响应。ERR_SSL_PROTOCOL_ERROR -
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与之对应】