Doris Java UDF 最佳实践

一 Java UDF简介

Java UDF 为用户提供 UDF 编写的 Java 接口,以方便用户使用 Java 语言进行自定义函数的执行。

1.1 UDF编写

使用 Java 代码编写 UDF,UDF 的主入口必须为 evaluate 函数。这一点与 Hive 等其他引擎保持一致。Doris 支持的 Java UDF,同时还是 Hive 支持的 UDF,也就是说,对于用户来讲,Hive UDF 是可以直接迁移至 Doris 的

例如,我们编写了 AddOne UDF 来完成对整型输入进行加一的操作。

public class AddOne extends UDF {
    public Integer evaluate(Integer value) {
        return value == null ? null : value + 1;
    }
}

编写完成后,编译打包成jar文件。实现的 jar 包可以放在本地也可以存放在远程服务端通过 HTTP 下载,但必须让每个 FE 和 BE 节点都能获取到 jar 包。

否则将会返回错误状态信息 Couldn't open file ......

如果定义的 UDF 中需要加载很大的资源文件,或者希望可以定义全局的 static 变量,可以参照下面的最佳实践。

1.2 创建UDF

CREATE FUNCTION java_udf_add_one(int) RETURNS int PROPERTIES (
    "file"="file:///path/to/java-udf-demo-jar-with-dependencies.jar",
    "symbol"="org.apache.doris.udf.AddOne",
    "always_nullable"="true",
    "type"="JAVA_UDF"
);

更多语法帮助可参阅 CREATE FUNCTION.

1.3 使用UDF

用户使用 UDF 必须拥有对应数据库的 SELECT 权限。

UDF 的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而 UDF 的作用域是 DB 内部。

当链接 session 位于数据内部时,直接使用 UDF 名字会在当前 DB 内部查找对应的 UDF。否则用户需要显示的指定 UDF 的数据库名字,例如 dbName.funcName

select java_udf_add_one(5) from tableName;

1.4 删除UDF

drop function java_udf_add_one(int);

二 最佳实践

本示例实现IP归属地查询,使用纯真社区版IP库,因此需要加载离线IP数据库文件,属于加载大的资源文件,因此需要使用static变量实现。

2.1 加载离线IP数据库

ip离线库使用查看:https://github.com/tagphi/czdb-search-java

通过 static 变量将ip离线库加载到内存。

Ip2Region.java

package com.sdpost;

import net.cz88.czdb.DbSearcher;
import net.cz88.czdb.QueryType;

import java.io.InputStream;
import java.util.Properties;

public class Ip2Region {
    private static DbSearcher dbSearcher;

    static {
        // 创建一个Properties对象
        Properties prop = new Properties();
        // 使用当前类的类加载器来获取资源文件的输入流
        InputStream propInputStream = Ip2Region.class.getClassLoader().getResourceAsStream("application.properties");

        if (propInputStream != null) {
            try {
                // 加载属性列表
                prop.load(propInputStream);

                // 获取属性值
                String dbFilePath = prop.getProperty("dbfile.path");
                dbSearcher = new DbSearcher(dbFilePath, QueryType.MEMORY, "xxxx");

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("无法找到配置文件");
        }
    }

    public static DbSearcher evaluate() {
        return dbSearcher;
    }
}

application.properties

dbfile.path= /opt/doris/czdb/cz88_public_v4.czdb

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>com.sdpost</groupId>
    <artifactId>ip2region</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.cz88</groupId>
            <artifactId>czdb-search</artifactId>
            <version>1.0.2.3</version>
        </dependency>
    </dependencies>

  <build>
      <plugins>

          <plugin>
              <artifactId>maven-assembly-plugin</artifactId>
              <configuration>
                  <descriptorRefs>
                      <descriptorRef>jar-with-dependencies</descriptorRef>
                  </descriptorRefs>
                  <archive>
                      <manifest>
                          <mainClass>com.sdpost.Main</mainClass>
                      </manifest>
                  </archive>
              </configuration>
              <executions>
                  <execution>
                      <phase>package</phase>
                      <goals>
                          <goal>single</goal>
                      </goals>
                  </execution>
              </executions>
          </plugin>
      </plugins>
  </build>

</project>

编译打包,生成ip2region-1.0-jar-with-dependencies.jar

需要将依赖包一块打包到jar,因为doris集群中没有相关的jar

2.2 编写 UDF

新建一个Project,将上面打包的jar包ip2region-1.0-jar-with-dependencies.jar导入到Project中。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>com.sdpost.udf</groupId>
    <artifactId>ip2region_udf</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>2.3.5</version>
            <exclusions>
                <exclusion>
                    <groupId>org.pentaho</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.sdpost</groupId>
            <artifactId>ip2region</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>com.sdpost.udf.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

编写UDF类 IpRegion.class

package com.sdpost.udf;

import com.sdpost.Ip2Region;
import net.cz88.czdb.DbSearcher;
import net.cz88.czdb.exception.IpFormatException;
import org.apache.hadoop.hive.ql.exec.UDF;

import java.io.IOException;

public class IpRegion extends UDF {
    public String evaluate(String value) throws IpFormatException, IOException {
        DbSearcher dbSearcher = Ip2Region.evaluate();
        return dbSearcher.search(value);
    }
}

编译打包生成 ip2region_udf-1.0.jar

此处不需要包含依赖包,因为doris集群中已经存在

2.3 部署UDF

由于想让资源 jar 包被所有的并发引用,所以需要将上面生成的 ip2region-1.0-jar-with-dependencies.jarip2region_udf-1.0.jar 放到Doris集群中指定路径 fe/custom_libbe/custom_lib 下面,服务重启之后就可以随着 JVM 的启动加载进来。

2.4 创建UDF

CREATE FUNCTION ip_to_region(string) RETURNS string PROPERTIES (
 "symbol"="com.sdpost.udf.IpRegion",
 "always_nullable"="true",
 "type"="JAVA_UDF"
);

--- 使用UDF
select ip_to_region("8.8.8.8") from tableName;

也可以使用 file:/// 方式自定义 ip2region_udf-1.0.jar 的路径,但是 ip2region-1.0-jar-with-dependencies.jar 只能放在custom_lib下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据AI

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值