개요

  • 임팔라는 hive에서 쓰던 java UDF 뿐만 아니라 C++로 UDF가 등록 가능합니다.
  • Hive에서 쓰던 UDF를 Impala에 사용하면 구글에 오픈소스가 많을 뿐더러, Java기반이기에 친숙하여 손쉽게 등록이 가능합니다. 단, 문제점이 하나 있는데, python(내부적으로 unicode 사용) 기반의 impala와 한글 호환이 정상적으로 이루어지지 않는것입니다.

1. Impala UDF 개발을 위한 환경 패키지 구축

  • Impala에 정상적으로 UDF 등록을 위해서는 선행작업 - 즉 개발을 위한 환경패키지를 구축해야합니다.
  • Version 정보를 정확하게 파악 후 진행하여줍니다.

http://archive.cloudera.com/cdh5 에서 버전에 맞는 impala-udf-devel 패키지를 다운받아 줍니다.

  • 제가 개발을 진행하는 DMP는 centos 6(Red Hat 계열 리눅스) / CDH 5.7.6 환경입니다.
#임팔라데몬이 설치된 데이터노드 접속
cd ~/stage
wget http://archive.cloudera.com/cdh5/redhat/6/x86_64/cdh/5.7.6/RPMS/x86_64/impala-udf-devel-2.5.0+cdh5.7.6+0-1.cdh5.7.6.p0.7.el6.x86_64.rpm

다운받은 rpm 패키지 설치.

# 기 설치 여부 확인
sudo rpm -qa | grep impala
>> 결과 없음.

# rpm 설치
sudo rpm -ivh impala-udf-devel-2.5.0+cdh5.7.6+0-1.cdh5.7.6.p0.7.el6.x86_64.rpm

# 설치 확인
rpm -qa | grep impala

# 설치된 패키지 경로 확인
rpm -ql impala-udf-devel-2.5.0+cdh5.7.6+0-1.cdh5.7.6.p0.7.el6.x86_64

2. impala UDF 샘플 코드 환경 구축.

  • 1번 과정을 통해 impala udf 개발을 위한 환경을 구축하였습니다.
  • rpm으로 설치된 패키지중 /usr/include/impala_udf/udf.h 는 가장 중요한 역할을합니다.
    • 스칼라 UDF를 작성하는 데 필요한 기본 선언이 명시됨.
    • AddUdf () 를 사용하며, UDF 작성 후 컴파일에 꼭 필요한 메소드.
  • 기본적인 개발 환경 구축을 하였으니, 컴파일이 바로 가능한 샘플 코드를 다운로드 해줍니다.

CDH 공식 github에서 impala UDF 샘플 코드 다운.

github 주소 : https://github.com/cloudera/impala-udf-samples

# 깃헙 클론
git clone https://github.com/cloudera/impala-udf-samples.git

3. impala UDF C++ 소스 작성.

  • 샘플 소스를 다운받은 뒤, CMakeLists.txt 에서 빌드할 소스를 자유롭게 등록 할 수 있습니다.
  • 우선, 소스목록편집/빌드 전에 C++ 기반의 소스를 작성해야합니다

헤더소스 url-coding.h (url decode 소스)

cd /home1/username/stage/impala-udf-samples
vi url-coding.h
#ifndef URLTOOL_UDF_H
#define URLTOOL_UDF_H

#include <impala_udf/udf.h>
#include 

using namespace impala_udf;
using namespace std;


StringVal UrlDecoder(FunctionContext* context, const StringVal& arg1);

StringVal UrlEncoder(FunctionContext* context, const StringVal& arg1);

std::string UrlEncode(const std::string& str);

std::string UrlDecode(const std::string& str);

unsigned char FromHex(unsigned char x);

unsigned char ToHex(unsigned char x);

#endif

메인소스 udf-urltool.cc (url decode 소스)

vi udf-urltool.cc
#include "udf-urltool.h"

#include 
#include 
#include 
#include 
using namespace std;


StringVal UrlDecoder(FunctionContext* context, const StringVal& arg1){
    if (arg1.is_null) return StringVal::null();

    try {
        std::string original((const char *)arg1.ptr,arg1.len);
        std::string decoded = UrlDecode(original);

        StringVal result(context, decoded.size());
        memcpy(result.ptr, decoded.c_str(), decoded.size());

        return result;
    }catch(...){
        return StringVal::null();
    }

}

StringVal UrlEncoder(FunctionContext* context, const StringVal& arg1){
    if (arg1.is_null) return StringVal::null();

    try {
        std::string original((const char *)arg1.ptr,arg1.len);
        std::string encoded = UrlEncode(original);

        StringVal result(context, encoded.size());
        memcpy(result.ptr, encoded.c_str(), encoded.size());

        return result;
    }catch(...){
        return StringVal::null();
    }
}




inline unsigned char ToHex(unsigned char x) {
    return  x > 9 ? x + 55 : x + 48;
}

inline unsigned char FromHex(unsigned char x) {
    unsigned char y;
    if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
    else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
    else if (x >= '0' && x <= '9') y = x - '0';
    else y = ' ';
    return y;
}

inline std::string UrlEncode(const std::string& str) {
    std::string strTemp = "";
    size_t length = str.length();
    for (size_t i = 0; i < length; i++)
    {
        if (isalnum((unsigned char)str[i]) ||
            (str[i] == '-') ||
            (str[i] == '_') ||
            (str[i] == '.') ||
            (str[i] == '~'))
            strTemp += str[i];
        else if (str[i] == ' ')
            strTemp += "+";
        else
        {
            strTemp += '%';
            strTemp += ToHex((unsigned char)str[i] >> 4);
            strTemp += ToHex((unsigned char)str[i] % 16);
        }
    }
    return strTemp;
}

inline std::string UrlDecode(const std::string& str) {
    std::string strTemp = "";
    size_t length = str.length();
    for (size_t i = 0; i < length; i++)
    {
        if (str[i] == '+') strTemp += ' ';
        else if (str[i] == '%')
        {
            unsigned char high = FromHex((unsigned char)str[++i]);
            unsigned char low = FromHex((unsigned char)str[++i]);
            strTemp += high*16 + low;
        }
        else strTemp += str[i];
    }
    return strTemp;
}

CMakeLists.txt 수정

  • 위 에서 설명한 /usr/include/impala_udf/udf.h 헤더를 이용하여 컴파일을 할 목록을 추려내는 CMakeLists.txt를 수정하여줍니다.
vi CMakeLists.txt
# Copyright 2012 Cloudera Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 2.6)

# 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")

# 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.
# Disable compiler optimizations because generated IR is optimized at runtime
set(IR_COMPILE_FLAGS "-emit-llvm" "-c")
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(udfurltool SHARED udf-urltool.cc)


# Custom targest to cross compile UDA/UDF to ir

if (CLANG_EXECUTABLE)
  COMPILE_TO_IR(udf-urltool.cc )
endif(CLANG_EXECUTABLE)


# This is an example of how to use the test harness to help develop UDF and UDAs.

target_link_libraries(udfurltool ImpalaUdf)

4. 컴파일 & HDFS 업로드

  • 위에서 url decode를 위한 헤더파일과 C++ 소스를 작성하였습니다.
  • CMakeLists.txt 에서 빌드할 소스목록도 편집하였습니다.
  • 소스 작성과 리스트를 편집하였으니, 빌드 후 HDFS에 업로드를 해야합니다.

build 파일 제작(컴파일)

cd /home1/username/stage/impala-udf-samples
cmake .
make

HDFS에 소켓 업로드

cd build
hadoop fs -put libudfurltool.so /tmp/

5. impala에 udf 등록

  • HDFS에 빌드한 소켓 업로드를 완료 하였으니, impala UDF를 등록 하여줍니다.
  • UDF 등록에 관한 자세한 내용은 [링크] 참조 부탁드립니다.

등록

impala-shell
...
[hdfs.datanode.host:21000] > 
create function urldecode_test (string) returns string location 'hdfs://hdfs.namenode.host:8020/tmp/libudfurltool.so' symbol='UrlDecoder';

테스트

select default.urldecode_test(encoded_text) as decoded_text from db.table;

>>>
+--------------+
| decoded_text |
+--------------+
| 테스트1   |
| 테스트2   |
| 테스트3   |
| 테스트4   |
| 테스트5   |
| 테스트6   |
| 테스트7   |
+----------+

:: reference

https://www.cloudera.com/documentation/enterprise/5-8-x/topics/impala\_udf.html
https://data-flair.training/blogs/impala-udf/

+ Recent posts