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的源码来编译时可行的。