C语言操作hdfs

需求

  • 在c++程序中操作hdfs实现数据的读取和写入。

目标

  • 写一个简单的c程序,将一句话写到hdfs上的一个文件中,并查看内容是否写进去了。

环境

  • 虚拟机:centos6.7
  • jdk :1.8
  • gcc: 4.8.5
  • hadoop:hadoop-2.7.7.tar.gz 、文档doc

思路

  • 安装hadoop
  • 在hdfs上创建一个文件:/liang/hello.txt
  • 编写writepro.c程序,程序中调用hdfs API将“Hello, World!”写到上述文件中。
  • 编译writepro.c成目标文件writepro
  • 运行writepro
  • 将hdfs上的/liang/hello.txt内容拷贝到本地,并使用cat命令查看内容。(完)

安装并运行

  • 解压hadoop-2.7.6.tar.gz到/usr路径下
  • 在etc/profile中配置HADOOP_HOME和 LD_LIBRARY_PATH
JAVA_HOME=/usr/java/jdk1.8.0_151
GCC_HOME=/usr/local/gcc-4.8.5
HADOOP_HOME=/usr/hadoop-2.7.6
PATH=$HADOOP_HOME/bin:$GCC_HOME/bin:$JAVA_HOME/bin:$PATH
export HADOOP_HOME GCC_HOME JAVA_HOME PATH
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server
  • 查看hadoop版本
[supdev@YZ-25-64-219]$ hadoop version
Hadoop 2.7.6
Subversion https://[email protected]/repos/asf/hadoop.git -r 085099c66cf28be31604560c376fa282e69282b8
Compiled by kshvachk on 2018-04-18T01:33Z
Compiled with protoc 2.5.0
From source with checksum 71e2695531cb3360ab74598755d036
This command was run using /usr/local/hadoop-2.7.6/share/hadoop/common/hadoop-common-2.7.6.jar

在hdfs上建文件

  • 伪分布式运行:Pseudo-Distributed Operation

  • 运行成功在浏览器进入 http://ip:50070 页面。(要开启linux上的50070端口)

    image.png

  • 在hdfs上创建/liang/hello.txt文件

#创建liang目录
hdfs dfs -mkdir /liang
#在本地创建hello.txt文件
touch hello.txt
#将hello.txt发送到/liang目录下
hdfs dfs -put hello.txt /liang
  • 验证文件已创建


    C语言操作hdfs_第1张图片
    image.png

编写writepro.c程序

  • 官方c api文档:C API libhdfs

  • 博客文档:c++ 操作HDFS、c++ 操作HDFS

  • 编程程序writepro.c,并将文件上传到linux上。

//hdfs操作api头文件
#include "hdfs.h"
#include 

int main(int argc, char **argv) {
    //连接hdfs
    hdfsFS fs = hdfsConnect("default", 0);
    //要写的文件路径
    const char* writePath = "/liang/hello.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);
  //flush一下
    if (hdfsFlush(fs, writeFile)) {
           fprintf(stderr, "Failed to 'flush' %s\n", writePath);
          exit(-1);
    }
 //关闭连接
    hdfsCloseFile(fs, writeFile);
}

使用gcc编译成writepro

  • 编译成最终执行文件的过程为:源码(.c)---编译-->目标文件(.o)---链接--->执行文件(后缀依据平台或编译器而定)。链接:将程序中使用的库函数与相关路径填入到目标文件中。

  • 要编译成执行文件,就需要指定hdfs.h头文件的位置,以及使用的动态链接库libhdfs.so和动态链接库的位置。动态链接库使用JNI技术实现了c调用java。

  • hdfs.h头文件位置在$HADOOP_HOME/include 下,编译时指定使用libhdfs.so动态链接库,libhdfs.so动态链接库在$HADOOP_HOME/lib/native目录下。

  • 程序运行过程中需要调用动态链接库,而动态链接库在磁盘中,而程序在内存中,速度不一致,所以要先将动态链接库先加载到高速缓存中。

  • 参考:

    • 1.后缀名“.dll .obj .lib”和“ .so .o .a”文件的区别含义
      1. 头文件、库文件、动态链接库文件之间的关系和作用的区分

动态链接库加载到缓存中

  • 知识点:ldconfig与/etc/ld.so.conf
  • ldconfig会将ld.so.conf中配置的目录下的动态链接库加载到高速缓存中
  • 打开ld.so.conf,添加$HADOOP_HOME/lib/native目录


    配置动态链接库
  • 使上面配置其作用
# 下面执行完后不会有任何信息显示
ldconfig
  • 可以使用下面命令查看所有被加载的动态链接库
ldconfig -p
显示格式:函数库名称 =》该函数库实际路径

编译成writepro文件

[root@CentOS usr]# gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro
writepro.c: 在函数‘main’中:
writepro.c:14:11: 警告:隐式声明与内建函数‘exit’不兼容 [默认启用]
           exit(-1);
           ^
writepro.c:19:71: 警告:隐式声明与内建函数‘strlen’不兼容 [默认启用]
     tSize num_written_bytes = hdfsWrite(fs, writeFile, (void*)buffer, strlen(buffer)+1);
                                                                       ^
writepro.c:23:11: 警告:隐式声明与内建函数‘exit’不兼容 [默认启用]
           exit(-1);
  • -I指定"hdfs.h"头文件位置、-l指定使用的动态链接库、-L指定到哪里去寻找使用的动态链接库。
    • -lhdfs是-l和hdfs两个部分组成,hdfs指明的是libhdfs.so这个函数库,其中lib和扩展名(.a或so)不用写。
  • 编译会有警告。

运行writepro

参考文档:CLASSPATH

  • libhdfs.so动态链接库实现了c调用hdfs java程序,即其依赖于java,所以hadoop的jar包和相关配置文件也就需要加载到内存中。为此在运行前需要配置CLASSPAT环境变量,这样在程序运行过程中就可以根据CLASSPATH指定的路径去加载jar和相关配置到内存,以提供c通过JNI调用。

  • 配置临时classpath

#hadoop classpath --glob命令会生成classpath所需内容
[root@CentOS /]# export CLASSPATH=`hadoop classpath --glob`
  • 运行程序
[root@CentOS /]# ./usr/writepro
18/04/26 23:59:47 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
  • 执行会有一个警告。

查看内容是否写入

  • 将hdfs上hello.txt内容读取到helloresult.txt中
[root@CentOS /]# hadoop fs -get /liang/hello.txt helloresult.txt
[root@CentOS /]# cat helloresult.txt
Hello, World!

总结

  • 不知道对不对


    C语言操作hdfs_第2张图片
    c-hdfs调用关系

libjvm.so动态链接库找不到问题

1.编译时报 libjvm.so动态链接库找不到

[root@CentOS usr]# gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro

/usr/bin/ld: warning: libjvm.so, needed by /usr/hadoop-2.7.6/lib/native/libhdfs.so, not found (try using -rpath or -rpath-link)
/usr/hadoop-2.7.6/lib/native/libhdfs.so: undefined reference to `JNI_CreateJavaVM@SUNWprivate_1.1'
/usr/hadoop-2.7.6/lib/native/libhdfs.so: undefined reference to `JNI_GetCreatedJavaVMs@SUNWprivate_1.1'
collect2: 错误:ld 返回 1

2.问题分析

  • libjvm.so位置:/usr/java/jdk1.8.0_151/jre/lib/amd64/server目录下

  • writepro依赖libhdfs.so,而libhdfs.so依赖libjvm.so,在编译时需要指定lib的路径,在执行时也需要指定寻找路径。

  • /usr/hadoop-2.7.6/lib/native/libhdfs.so依赖libjvm.so,动态链接库,而libjvm.so找不到。在编译时通过-Wl,rpath、LD_LIBRARY_PATH、ld.so.conf等配置libjvm.so路径。

解决方法

  • 通过LD_LIBRARY_PATH环境变量指定libjvm寻找路径(上面编译就是采用这一种)
# 指定编译和执行寻找libjvm路径
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro
  • 或者编译时通过-Wl,rpath指定寻找路径
#指定编译和执行寻找libjvm路径
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -Wl,-rpath=${JAVA_HOME}/jre/lib/amd64/server -o writepro
  • 或者在ld.so.conf中配置运行时寻找路径
#-ljvm -L${JAVA_HOME}/jre/lib/amd64/server让其编译通过
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -ljvm -L${JAVA_HOME}/jre/lib/amd64/server -o writepro
#在ld.so.conf中添加libjvm路径让其其执行通过,配置完要执行ldconfig
[root@CentOS usr]# vim /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/hadoop-2.7.6/lib/native
/usr/java/jdk1.8.0_151/jre/lib/amd64/server

查看writepro所依赖的动态函数库

  • 上述三种方案编译后的writepro都可以正常执行,并且不冲突。下面查看writepro和libhdfs.so所依赖的动态链接库。
# 库=》对应路径(地址)
[root@CentOS usr]# ldd writepro
    linux-vdso.so.1 =>  (0x00007ffe329d6000)
    libhdfs.so.0.0.0 => /usr/hadoop-2.7.6/lib/native/libhdfs.so.0.0.0 (0x00007f32dbfdb000)
    libc.so.6 => /lib64/libc.so.6 (0x000000310f800000)
    libjvm.so => /usr/java/jdk1.8.0_151/jre/lib/amd64/server/libjvm.so (0x00007f32dafe3000)
    libdl.so.2 => /lib64/libdl.so.2 (0x0000003110000000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x000000310fc00000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
    libm.so.6 => /lib64/libm.so.6 (0x0000003110800000)

root@CentOS usr]# ldd /usr/hadoop-2.7.6/lib/native/libhdfs.so
    linux-vdso.so.1 =>  (0x00007fff8b31c000)
    libjvm.so => /usr/java/jdk1.8.0_151/jre/lib/amd64/server/libjvm.so (0x00007fb42669f000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fb42649a000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb42627d000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fb425ee9000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
    libm.so.6 => /lib64/libm.so.6 (0x00007fb425c64000)

执行writepro出错

  • writepro编译完成后,对于通过LD_LIBRARY_PATH和ld.so.conf方案解决libjvm问题的情况,如果将LD_LIBRARY_PATH和ld.so.conf对应的libjvm内容去掉,再次执行writepro时就会出现如下错误:
[root@CentOS usr]# ./writepro 
./writepro: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory
  • 查询ldd writepro也会出现not found问题
[root@CentOS usr]# ldd writepro
    linux-vdso.so.1 =>  (0x00007ffdf21c6000)
    libhdfs.so.0.0.0 => /usr/hadoop-2.7.6/lib/native/libhdfs.so.0.0.0 (0x00007f50526b9000)
    libjvm.so => not found
    libc.so.6 => /lib64/libc.so.6 (0x000000310f800000)
    libjvm.so => not found
    libdl.so.2 => /lib64/libdl.so.2 (0x0000003110000000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x000000310fc00000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
  • 解决办法:在LD_LIBRARY_PATH和ld.so.conf任何一个地方配置libjvm.so路径就可以,无需重新编译。

  • 对于通过-Wl,rpath方式,如果libjvm.so路径变了,可以配置LD_LIBRARY_PATH和ld.so.conf来解决。或者重新编译指定新的路径。

参考

  • GCC 参数详解
  • 后缀名“.dll .obj .lib”和“ .so .o .a”文件的区别含义
  • 头文件、库文件、动态链接库文件之间的关系和作用的区分
  • C API libhdfs
  • c++ 操作HDFS
  • HDFS文件读写 使用c api
  • Hadoop文档doc
  • CLASSPATH
  • C API libhdfs
  • 静态库、共享库、动态库的创建和--动态链接库命名
  • linking with -Wl,-rpath and $(prefix)

你可能感兴趣的:(C语言操作hdfs)