C语言操作hdfs

作者: zlcook | 来源:发表于2018-04-26 17:58 被阅读26次

    需求

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

    目标

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

    环境

    思路

    • 安装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
    

    在hdfs上建文件

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


      image.png

    编写writepro.c程序

    //hdfs操作api头文件
    #include "hdfs.h"
    #include <stdio.h>
    
    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目录下。

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

    • 参考:

    动态链接库加载到缓存中

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


      配置动态链接库
    • 使上面配置其作用
    # 下面执行完后不会有任何信息显示
    ldconfig
    
    • 可以使用下面命令查看所有被加载的动态链接库
    idconfig -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调用关系

    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来解决。或者重新编译指定新的路径。

    参考

    相关文章

      网友评论

        本文标题:C语言操作hdfs

        本文链接:https://www.haomeiwen.com/subject/bglmlftx.html