HiveServer2 用户自定义函数加载本地库出现 UnsatisfiedLinkError 的解决方案

创建项目 hive-func,创建函数 CPlusUtil 实现两个整数的加法运算。代码如下:

CPlusUtil.java

package com.baidu.hive.func;

import org.apache.hadoop.hive.ql.exec.UDF;

public class CPlus extends UDF {

    public int evaluate(int a, int b) {
        return CPlusUtil.add(a, b);
    }
}

CPlus 调用 CPlusUtil 的静态方法。

CPlusUtil.java

CPlusUtil 的静态方法是 native 的。

package com.baidu.hive.func;

public class CPlusUtil {

    static {
        System.loadLibrary("cplus");
    }

    public static native int add(int a, int b);
}

生成 hive-func-0.1.0.jar

编译项目,生成 target/hive-func-0.1.0.jar 文件。

mvn clean package

生成 so 文件

先生成头文件

cd target/classes/
javah com.baidu.hive.func.CPlusUtil

查看头文件的内容。

com_baidu_hive_func_CPlusUtil.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_baidu_hive_func_CPlusUtil */

#ifndef _Included_com_baidu_hive_func_CPlusUtil
#define _Included_com_baidu_hive_func_CPlusUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_baidu_hive_func_CPlusUtil
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_baidu_hive_func_CPlusUtil_add
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

我们在和头文件同目录下创建 CPlusUtil.c 文件,内容如下:

CPlusUtil.c

#include <jni.h>        // JNI header provided by JDK
#include <stdio.h>      // C Standard IO Header
#include "com_baidu_hive_func_CPlusUtil.h"   // Generated
 

JNIEXPORT jint JNICALL Java_com_baidu_hive_func_CPlusUtil_add
  (JNIEnv *env, jclass thisObj, jint a, jint b) {
   return a+b;
};

用以下命令生成 so 文件

gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcplus.so CPlusUtil.c

注意:java 里加载的是 cplus,生成的文件是 libcplus.so

拷贝文件到 Hadoop 集群

hadoop fs -mkdir -p /apps/hive;
hadoop fs -put hive-func-0.1.0.jar /apps/hive;
hadoop fs -put libcplus.so /apps/hive;

用 beeline 进行第 1 次测试

  • 第一次可以执行。
create function default.cplus as 'com.baidu.hive.func.CPlus' 
using jar 'hdfs://master-e285d25:8020/apps/hive/hive-func-0.1.0.jar',  
file 'hdfs://master-e285d25:8020/apps/hive/libcplus.so';

select default.cplus(1,2);
  • 退出 beeline
!q

用 beeline 进行第 2 次测试

select default.cplus(1,2);

HiveServer 后台抛出异常 UnsatisfiedLinkError

使用 hive-cli 正常

直接执行 hive 命令,可以正常使用函数。

原因分析

为了避免不同 session 之间的 add jar 造成的相互影响,HiveServer 每个 session 使用一个 class loader。使用函数的时候,从 hdfs 下载 jar 包,修改 session 的 class loader 的 uris,让 session 的 class loader 知道从哪里读取文件。第 1 次运行的时候,调用 System.loadLibrary("cplus"); 是正确的。再次打开一个会话,使用了新的 class loader,认为 CPlus 和 CPlusUtil 类没有加载,造成 CPlus 和 CPlusUtil 两个类都重新加载一遍,本次加载执行 System.loadLibrary("cplus"); 失败。

解决方案 1

  1. 把 libcplus.so 拷贝到 ${HADOOP_HOME}/lib/native 目录下

  2. 把 hive-func-0.1.0.jar 拷贝到 ${HIVE_HOME}/lib 目录下

  3. 重启 hive server2

  4. 测试 hiveserver,多次进出 beeline,执行以下语句,都可以正确执行。

select default.cplus(1,2);
  1. 测试分布式任务,可以正确执行。
create table t1 as select default.cplus(c1, c2) from tableName;

解决方案 1 总结

  1. 拷贝文件到 HiveServer 所在的本地文件系统是为了解决 HiveServer 的执行环境问题。
  2. 使用 HDFS 上的文件是为了解决分布式执行环境的问题。
  3. 如果 so 文件或者 jar 文件有修改,需要同时更新 HiveServer 的环境和 HDFS 上的文件,重启 HiveServer。

解决方案2

修改 c 代码,在 so 里判断本进程是否已经加载 so 文件,保证 so 文件只加载一次。使用本方案,不需要在 HiveServer 里放 jar 包和 so 文件。以后函数更新仅用更新 HDFS 上的文件,比较方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值