0518-如何在Impala中使用UDF获取SessionId
- 2019 年 11 月 27 日
- 笔记
1
文档编写目的
Hive在UDF中获取sessionId可以直接使用提供的java API,但是该UDF如果移植到Impala中是无法获取到Impala连接的SessionId的,要想获取Impala的SessionId,需要用C++来编写。
2
实现思路
1. 根据Impala的源码可知可通过UDF参数中的context上下文对象来取得该SessionId,具体调用关系如下:
context->impl()->state()->session_id();
源码参考:
https://github.com/cloudera/Impala/blob/cdh5-2.9.0_5.12.2/be/src/udf/udf.h#L234 https://github.com/cloudera/Impala/blob/cdh5-2.9.0_5.12.2/be/src/runtime/runtime-state.h#L112
但是从源码中方法的注释上我们可以看到,context的impl()方法是不允许在UDA/UDF中使用的:

因此从官方提供的UDF依赖包impala-udf-devel安装后的库中也可以看到,并没有runtime-state.h等编译需要的依赖文件:

上图的五个声明文件是官方提供的UDF依赖。
按照正常的Impala用C++来实现UDF的流程,context->impl()->state()->session_id()将不能编译成功。

2. 因此在编译该UDF文件时,只能通过引入impala的源码和编译源码时需要的依赖来实现。
3
编译流程
1. 安装maven
下载maven:
wget http://mirror.cogentco.com/pub/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz

解压:
tar -zxvf apache-maven-3.6.0-bin.tar.gz mv apache-maven-3.6.0 /usr/local/

修改/etc/profile添加到环境变量
export MAVEN_HOME=/usr/local/apache-maven-3.6.0
另外,如果有配anaconda的环境变量,暂时注释

验证maven:

2. 升级cmake
下载cmake-3.3.2-1.gf.el7.x86_64.rpm,并制作本地yum源,可到https://pkgs.org下载:
yum -y install cmake-3.3.2-1.gf.el7.x86_64
3. 安装高版本的g++编译器,impala源码中引入了c++11新特性,g++太低将无法编译,此处安装的是7.3.1版本。
sudo yum -y install centos-release-scl sudo yum -y install devtoolset-7 scl enable devtoolset-7 bash
验证g++:

4. 下载Impala源码
wget https://codeload.github.com/cloudera/Impala/zip/cdh5-trunk
解压到当前目录
unzip Impala-cdh5-trunk.zip
将该目录添加到环境变量IMPALA_HOME
export IMPALA_HOME=/data/Impala-cdh5-trunk

source /etc/profile
5. 安装依赖
yum install -y redhat-lsb.x86_64 yum -y group install "Development Tools" yum -y install git ant libevent-devel automake libtool flex bison gcc-c++ yum -y install openssl-devel make yum -y install doxygen.x86_64 glib-devel python-devel bzip2-devel svn libevent-devel krb5-workstation yum -y install openldap-devel db4-devel python-setuptools python-pip cyrus-sasl* postgresql postgresql-server ant-nodeps lzo-devel lzop yum -y install boost boost-devel
6. 编译源码
进入impala源码目录
./buildall.sh -notests(该命令将编译整个工程,耗时较长) 或者执行: ./buildall.sh -cmake_only
上述命令执行完毕之后检查以下几点:
be/generated-sources下是否有gen-cpp目录及内部是否有文件:

toolchain下是否已下载thrift,glog,gflags,rapidjson依赖

7. 创建UDF文件
创建文件夹:
mkdir -p /root/impala-udf mkdir -p /root/impala-udf/include
将源码包内相关包复制到include下
cp -r toolchain/gflags-2.2.0-p1/include/gflags/ /root/impala-udf/include cp -r toolchain/glog-0.3.4-p3/include/glog/ /root/impala-udf/include cp -r toolchain/rapidjson-0.11/include/rapidjson/ /root/impala-udf/include cp -r toolchain/thrift-0.9.0-p11/include/thrift/ /root/impala-udf/include cp -r be/generated-sources/gen-cpp /root/impala-udf/include cp -r be/src/* /root/impala-udf/include
编写获取SessionId的C++代码:
编写getSessionId.h
#ifndef SAMPLES_UDF_H #define SAMPLES_UDF_H #include <udf/udf.h> #include <udf/udf-internal.h> using namespace impala_udf; StringVal GetSessionId(FunctionContext* context); #endif
编写getSessionId.cc
从下方的Impala的JAVA源码中可以看到,SessionId存放在TUniqueId对象的两个字段中,取出时需将其转换成16进制字符串:

此处从context中取得TuniqueId对象之后,将其转化成16进制。
#define __STDC_FORMAT_MACROS #include <stdlib.h> #include "getSessionId.h" #include <udf/udf.h> #include <udf/udf-internal.h> #include <runtime/runtime-state.h> #include <string.h> #include <vector> #include <stdio.h> #include <inttypes.h> #include<iostream> using namespace std; namespace impala{ class TUniqueId; } using namespace impala_udf; string DecIntToHexStr(unsigned long long num) { string str; long long Temp = num / 16; int left = num % 16; if (Temp > 0) str += DecIntToHexStr(Temp); if (left < 10) str += (left + '0'); else str += ('A' + left - 10); return str; } StringVal getSessionId(FunctionContext* context) { impala::TUniqueId id = context->impl()->state()->session_id(); string idhi = DecIntToHexStr(id.hi); string idlo = DecIntToHexStr(id.lo); string sessionid = idhi +":"+ idlo; return *(new StringVal(sessionid.data())); }
编写CMakeList.txt文件:
cmake_minimum_required(VERSION 3.2.3) # where to put generated libraries set(LIBRARY_OUTPUT_PATH "build") # where to put generated binaries set(EXECUTABLE_OUTPUT_PATH "build") find_program(CLANG_EXECUTABLE clang++) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb") include_directories("include") # Function to generate rule to cross compile a source file to an IR module. # This should be called with the .cc src file and it will generate a # src-file-ir target that can be built. # e.g. COMPILE_TO_IR(test.cc) generates the "test-ir" make target. set(IR_COMPILE_FLAGS "-emit-llvm" "-O3" "-c" "-Iinclude") function(COMPILE_TO_IR SRC_FILE) get_filename_component(BASE_NAME ${SRC_FILE} NAME_WE) set(OUTPUT_FILE "build/${BASE_NAME}.ll") add_custom_command( OUTPUT ${OUTPUT_FILE} COMMAND ${CLANG_EXECUTABLE} ${IR_COMPILE_FLAGS} ${SRC_FILE} -o ${OUTPUT_FILE} DEPENDS ${SRC_FILE}) add_custom_target(${BASE_NAME}-ir ALL DEPENDS ${OUTPUT_FILE}) endfunction(COMPILE_TO_IR) # Build the UDA/UDFs into a shared library. add_library(getSessionId SHARED getSessionId.cc) # Custom targest to cross compile UDA/UDF to ir if (CLANG_EXECUTABLE) COMPILE_TO_IR(getSessionId.cc ) endif(CLANG_EXECUTABLE) # This is an example of how to use the test harness to help develop UDF and UDAs. target_link_libraries(getSessionId ImpalaUdf)
8. 编译UDF
cmake .

make

在该目录的build下可看见编译好的文件

4
验证UDF
1. 将libgetSessionId.so上传hdfs的/tmp下:
hdfs dfs -put libgetSessionId.so /tmp
2. 创建UDF并验证
CREATE FUNCTION getSessionId() RETURNS string location '/tmp/libgetSessionId.so' symbol='getSessionId';
查询
select *,getSessionId() from test_table;

从上图中可以看出,在一次查询中获取的sessionId相同。
断开连接后再次连接查询:

可以看到,与上一次连接相比,SessionId已发生改变。
3. 但是impala不同于hive,在cmz中impala的log不能看到impala的Sessionid,因此,不容易验证获取的正确性,但impala的log中有查询的query_id。
此处我们将context->impl()->state()->session_id();改为context->impl()->state()->query_id();重新编译后上传创建getQueryId()。
CREATE FUNCTION getQueryId() RETURNS string location '/tmp/libgetQueryId.so' symbol='getQueryId';
查询:
select *,getQueryId() from test_table;

可看到在两次的查询中query_id不同,符合预期,并且与log日志中的query_id相同:


5
总结
IMPALA的UDF,不论是java还是c++,都不能操作session。当官方提供的UDF API不能够满足需求时,引入IMPALA的源码来编译时可行的。