[1.2.0新功能系列:三]Apache doris 1.2.0 Java UDF 函数开发及使用

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: [1.2.0新功能系列:三]Apache doris 1.2.0 Java UDF 函数开发及使用

概述


我们在使用各个SQL引擎时,会有纷繁复杂的查询需求。一部分可以通过引擎自带的内置函数去解决,但内置函数不可能解决所有人的问题,所以一般SQL引擎会提供UDF功能,方便用户通过自己写逻辑来满足特定的需求,Doris也不例外。


在java UDF之前,Doris提供了两种用户可以自己实现UDF的方式:


远程UDF,其优缺点如下:


支持通过 RPC 的方式访问用户提供的 UDF Service,以实现用户自定义函数的执行


只要支持Protobuf的各类语言都能使用,有足够的安全和灵活性


额外的网络开销和基于protobuf的开发模式让该使用方式的用户望而却步

原生UDF,其优缺点如下:


支持使用C++编写UDF,执行效率高、速度快


跟Doris代码耦合度高,需要自己打包编译Doris源码


只支持C++语言并且容易造成BE挂掉


熟悉大数据组件(Hive Spark等)的用户有一定的门槛


看起来上述UDF的两种方式实现起来有点复杂。有没有相对简单,门槛较低,跟Doris代码耦合度低,对Java友好的UDF方式呢?


在 Doris 1.2.0 版本我们正式支持 Java UDF 函数,你可以像之前写 Hive udf函数一样去写自己的Doris udf函数来处理自己复杂的业务逻辑。


SinceVersion 1.2.0


Java UDF 为用户提供UDF编写的Java接口,以方便用户使用Java语言进行自定义函数的执行。相比于 Native 的 UDF 实现,Java UDF 有如下优势和限制:


优势


兼容性:使用Java UDF可以兼容不同的Doris版本,所以在进行Doris版本升级时,Java UDF不需要进行额外的迁移操作。与此同时,Java UDF同样遵循了和Hive/Spark等引擎同样的编程规范,使得用户可以直接将Hive/Spark的UDF jar包迁移至Doris使用。


安全:Java UDF 执行失败或崩溃仅会导致JVM报错,而不会导致 Doris 进程崩溃。


灵活:Java UDF 中用户通过把第三方依赖打进用户jar包,而不需要额外处理引入的三方库。


使用限制


性能:相比于 Native UDF,Java UDF会带来额外的JNI开销,不过通过批式执行的方式,我们已经尽可能的将JNI开销降到最低。


向量化引擎:Java UDF当前只支持向量化引擎。


doris 提供


UDF:用户自定义函数,user defined function。一对一的输入输出,(最常用的)。


UDAF:用户自定义聚合函数。user defined aggregate function,多对一的输入输出,类似 count sum max 等统计函数


怎么实现 Doris Java UDF函数


下面我们来开始讲解怎么编写和使用 doris java udf函数。


Doris java udf 函数是基于 Hive udf 框架来实现的


继承org.apache.hadoop.hive.ql.exec.UDF


重写evaluate(),


特殊说明:


evaluate()方法不是由接口定义的,因为它可接受的参数个数,数据类型都是不确定的。Doris 会检查UDF, 看能否找到和函数调用相匹配的evaluate()方法


这里演示的是我们怎么实现一个 AES 加解密的函数


函数开发


我们创建一个普通的java maven 工程


pom.xml依赖如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>org.apache.doris</groupId>
 <artifactId>doris.java.udf.demo</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
 <name>doris.java.udf.demo</name>
 <url>http://maven.apache.org</url>
 <properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>
 <dependencies>
   <dependency>
     <groupId>org.apache.hive</groupId>
     <artifactId>hive-exec</artifactId>
     <version>2.3.5</version>
   </dependency>
 </dependencies>
 <build>
   <finalName>java-udf-demo</finalName>
   <plugins>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-jar-plugin</artifactId>
       <version>3.2.2</version>
     </plugin>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-assembly-plugin</artifactId>
       <version>3.3.0</version>
       <configuration>
         <descriptorRefs>
           <descriptorRef>jar-with-dependencies</descriptorRef>
         </descriptorRefs>
       </configuration>
       <executions>
         <execution>
           <phase>package</phase>
           <goals>
             <goal>single</goal>
           </goals>
         </execution>
       </executions>
     </plugin>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
         <source>8</source>
         <target>8</target>
       </configuration>
     </plugin>
   </plugins>
 </build>
</project>

加解密工具类:

package org.apache.doris.udf.demo;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import java.security.SecureRandom;
/**
* AES encryption and decryption tool class
*
* @author zhangfeng
*/
public class AESUtil {
   private static final String defaultCharset = "UTF-8";
   private static final String KEY_AES = "AES";
   /**
    * AES encryption function method
    *
    * @param content
    * @param secret
    * @return
    */
   public static String encrypt(String content, String secret) {
       return doAES(content, secret, Cipher.ENCRYPT_MODE);
  }
   /**
    * AES decryption function method
    *
    * @param content
    * @param secret
    * @return
    */
   public static String decrypt(String content, String secret) {
       return doAES(content, secret, Cipher.DECRYPT_MODE);
  }
   /**
    * encryption and decryption
    *
    * @param content
    * @param secret
    * @param mode
    * @return
    */
   private static String doAES(String content, String secret, int mode) {
       try {
           if (StringUtils.isBlank(content) || StringUtils.isBlank(secret)) {
               return null;
          }
           //Determine whether to encrypt or decrypt
           boolean encrypt = mode == Cipher.ENCRYPT_MODE;
           byte[] data;
           //1.Construct a key generator, specified as the AES algorithm, case-insensitive
           KeyGenerator kgen = KeyGenerator.getInstance(KEY_AES);
           SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
           //2. Initialize the key generator according to the ecnodeRules rules
           //Generate a 128-bit random source, based on the incoming byte array
           secureRandom.setSeed(secret.getBytes());
           //Generate a 128-bit random source, based on the incoming byte array
           kgen.init(128, secureRandom);
           //3.generate the original symmetric key
           SecretKey secretKey = kgen.generateKey();
           //4.Get the byte array of the original symmetric key
           byte[] enCodeFormat = secretKey.getEncoded();
           //5.Generate AES key from byte array
           SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, KEY_AES);
           //6.According to the specified algorithm AES self-generated cipher
           Cipher cipher = Cipher.getInstance(KEY_AES);
           //7.Initialize the cipher, the first parameter is encryption (Encrypt_mode) or decryption (Decrypt_mode) operation,
           // the second parameter is the KEY used
           cipher.init(mode, keySpec);
           if (encrypt) {
               data = content.getBytes(defaultCharset);
          } else {
               data = parseHexStr2Byte(content);
          }
           byte[] result = cipher.doFinal(data);
           if (encrypt) {
               //convert binary to hexadecimal
               return parseByte2HexStr(result);
          } else {
               return new String(result, defaultCharset);
          }
      } catch (Exception e) {
           System.out.println(e.getMessage());
      }
       return null;
  }
   /**
    * convert binary to hexadecimal
    *
    * @param buf
    * @return
    */
   public static String parseByte2HexStr(byte buf[]) {
       StringBuilder sb = new StringBuilder();
       for (int i = 0; i < buf.length; i++) {
           String hex = Integer.toHexString(buf[i] & 0xFF);
           if (hex.length() == 1) {
               hex = '0' + hex;
          }
           sb.append(hex.toUpperCase());
      }
       return sb.toString();
  }
   /**
    * Convert hexadecimal to binary
    *
    * @param hexStr
    * @return
    */
   public static byte[] parseHexStr2Byte(String hexStr) {
       if (hexStr.length() < 1) {
           return null;
      }
       byte[] result = new byte[hexStr.length() / 2];
       for (int i = 0; i < hexStr.length() / 2; i++) {
           int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
           int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
           result[i] = (byte) (high * 16 + low);
      }
       return result;
  }
}

加密函数

package org.apache.doris.udf.demo;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.commons.lang3.StringUtils;
public class AESEncrypt extends UDF {
   public String evaluate(String content, String secret) throws Exception {
       if (StringUtils.isBlank(content)) {
           throw new Exception("content not is null");
      }
       if (StringUtils.isBlank(secret)) {
           throw new Exception("Secret not is null");
      }
       return AESUtil.encrypt(content, secret);
  }
}

解密函数

package org.apache.doris.udf.demo;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.commons.lang3.StringUtils;
public class AESDecrypt extends UDF {
   public String evaluate(String content, String secret) throws Exception {
       if (StringUtils.isBlank(content)) {
           throw new Exception("content not is null");
      }
       if (StringUtils.isBlank(secret)) {
           throw new Exception("Secret not is null");
      }
       return AESUtil.decrypt(content, secret);
  }
}

函数打包

mvn clean package

这个时候我们可以得到一个 java-udf-demo.jar


注册函数


注册加密函数


这里有两个参数,一个是加密内容,一个是秘钥,返回值是一个字符串

CREATE FUNCTION ase_encryp(string,string) RETURNS string PROPERTIES (
   "file"="file:///Users/zhangfeng/work/doris.java.udf.demo/target/java-udf-demo.jar",
   "symbol"="org.apache.doris.udf.demo.AESEncrypt",
   "always_nullable"="true",
   "type"="JAVA_UDF"
);

注意:


这里我是单机测试,使用的是本地文件方式,如果你也是要本地文件方式需要再所有的 FE 及 BE 上相同目录下都要有这个文件


我们也可以使用http方式,让每个节点自己下载这个文件,我们更推荐这种方式,下面也给出这种方式的示例


Http 方式示例:

CREATE FUNCTION ase_encryp(string,string) RETURNS string PROPERTIES (
   "file"="http://192.168.31.54/work/doris.java.udf.demo/target/java-udf-demo.jar",
   "symbol"="org.apache.doris.udf.demo.AESEncrypt",
   "always_nullable"="true",
   "type"="JAVA_UDF"
);

然后我们执行我们刚才创建的函数


要加密的内容是:zhangfeng,秘钥是: java_udf_function

select ase_encryp('zhangfeng','java_udf_function');

从下图可以看到我们得到了加密后的结果

40.png注册解密函数

CREATE FUNCTION ase_decryp(string,string) RETURNS string PROPERTIES (
  "file"="file:///Users/zhangfeng/work/doris.java.udf.demo/target/java-udf-demo.jar",
  "symbol"="org.apache.doris.udf.demo.AESDecrypt",
  "always_nullable"="true",
  "type"="JAVA_UDF"
);

http方式:

CREATE FUNCTION ase_decryp(string,string) RETURNS string PROPERTIES (
  "file"="http://192.168.63.32/work/doris.java.udf.demo/target/java-udf-demo.jar",
  "symbol"="org.apache.doris.udf.demo.AESDecrypt",
  "always_nullable"="true",
  "type"="JAVA_UDF"
);

验证函数


我们对上面解密的结果进行解密操作

select ase_decryp('4442106BB8C98E74D19CEC0413467810','java_udf_function');

可以看到我们得到了正确的解密结果

41.png

目录
相关文章
|
17天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
3天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
28天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
111 13
|
1月前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
73 10
|
26天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
62 2
|
5月前
|
存储 消息中间件 Java
Apache Flink 实践问题之原生TM UI日志问题如何解决
Apache Flink 实践问题之原生TM UI日志问题如何解决
53 1
|
28天前
|
存储 人工智能 大数据
The Past, Present and Future of Apache Flink
本文整理自阿里云开源大数据负责人王峰(莫问)在 Flink Forward Asia 2024 上海站主论坛开场的分享,今年正值 Flink 开源项目诞生的第 10 周年,借此时机,王峰回顾了 Flink 在过去 10 年的发展历程以及 Flink社区当前最新的技术成果,最后展望下一个十年 Flink 路向何方。
316 33
The Past, Present and Future of Apache Flink
|
3月前
|
SQL Java API
Apache Flink 2.0-preview released
Apache Flink 社区正积极筹备 Flink 2.0 的发布,这是自 Flink 1.0 发布以来的首个重大更新。Flink 2.0 将引入多项激动人心的功能和改进,包括存算分离状态管理、物化表、批作业自适应执行等,同时也包含了一些不兼容的变更。目前提供的预览版旨在让用户提前尝试新功能并收集反馈,但不建议在生产环境中使用。
908 13
Apache Flink 2.0-preview released
|
3月前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
121 3
|
4月前
|
SQL 消息中间件 关系型数据库
Apache Doris Flink Connector 24.0.0 版本正式发布
该版本新增了对 Flink 1.20 的支持,并支持通过 Arrow Flight SQL 高速读取 Doris 中数据。

推荐镜像

更多