文章目录
- 一、目的
- 二、修改计划
- 外部需求(背景)
- 1、External lib registration(外部lib注册)
- 2、Language Distinction(语言差异)
- 3、Temporary Function Support(支持注册临时函数)
- 4、Function Qualifier(函数限定符)
- 三、Function DDL语法
- 创建函数语句(Create Function Statement)
- 删除函数语句(Drop Function Statement)
- 修改函数语句(Alter Function Statement)
- 显示函数语句(Show Function Statement)
- 四、使用场景
- 从classpath加载UDF
- 从远程资源加载UDF
- 从远程资源加载python UDF
- 五、新增或更改的公共接口
- 六、资源隔离
- 七、实施计划
- Flink 1.10 Release
- Flink 1.10后续版本
- 八、参考
- 九、社区issue和pr进展(总结,非翻译)
- 1、支持创建、删除、修改函数语法(FLink 1.10.0可用)
- 2、为function DDL添加“USING JAR/FILE/ACHIVE”功能(FLink 1.11.0可用)
- 3、 在{Stream}ExecutionEnvironment中注册用户jar文件(FLink 1.10.0可用)
一、目的
FLIP-79目的在于,支持function DDL,同时考虑SQL语法、语义的正确性,并进一步,可以从外部lib(jar或文件),注册UDF。
Flink DDL是由Chen Shuyi等人在design[1]中进行初步讨论,初步讨论主要集中在Table(表)、Type(类型)和View(视图)上。flip69[2]对DDL进行了扩展,对catalog、database和function(函数)进行了更详细的讨论。function DDL也属于FLIP-69范畴。
经过和社区讨论,我们发现几个正在进行的工作,例如flip64[3]、flip65[4]和flip78[5],他们将直接影响function DDL的SQL语法,因此当前FLIP结合已有的工作,对问题进行清晰的描述,并确保当前设计与已有成果对齐,即对齐不同语言UDF中定义的:temporary objects(临时对象)、type inference(类型接口)
二、修改计划
外部需求(背景)
在深入讨论DDL SQL之前,我们想讨论一下,相关FLIP在Flink runtime function方面主要需求:
1、External lib registration(外部lib注册)
这个需求来自于hive集成,它增强了Flink批处理功能。HQL(HIVE SQL)支持类似的语法如下:
CREATE FUNCTION addfunc AS ‘com.example.hiveserver2.udf.add’ USING JAR ‘hdfs:///path/to/jar’
2、Language Distinction(语言差异)
由于Scala中字节码语言的特殊性,从Scala函数中提取类型信息(TypeInformation)存在一些限制。同时,在table runtime(运行时)中,支持python UDF是另一个正在进行的工作。
因此,SQL语法需要考虑支持多种语言。
例如:Mysql创建函数语法支持这样的语言:
CREATE FUNCTION hello (s CHAR(20))RETURNS CHAR(50)DETERMINISTIC RETURN CONCAT(‘Hello, ‘,s,’!’) LANGUAGE SQL
3、Temporary Function Support(支持注册临时函数)
FLIP-57建议将临时函数和非临时函数分别用于catalog和system。由于temporary function(临时函数)只注册到当前session(会话)。因此需要一个来自DDL的flag(标记),来区分function的解析顺序。
CREATE TEMPORARY FUNCTION addfunc AS ‘com.example.hiveserver2.udf.add’ USING JAR ‘hdfs:///path/to/jar’
4、Function Qualifier(函数限定符)
函数限定符(.)的作用域包括:A、特定的catalog、database B、当前catalog和database。
因此,所有的function DDL都需要支持以下3部分路径(catalog1.db1.addfunc):
CREATE FUNCTION catalog1.db1.addfunc AS ‘com.example.hiveserver2.udf.add’ LANGUAGE JVM
三、Function DDL语法
我们提出function DDL语法如下:
创建函数语句(Create Function Statement)
CREATE [TEMPORARY|SYSTEM] FUNCTION [IF NOT EXISTS] [catalog_name.db_name.]function_name AS identifier [LANGUAGE JVM|PYTHON] [USING JAR|FILE|ARCHIVE ‘resource_path’ [, USING JAR|FILE|ARCHIVE ‘path’]*];
删除函数语句(Drop Function Statement)
DROP [TEMPORARY|SYSTEM] FUNCTION [IF EXISTS] [catalog_name.][db_name.] function_name;
修改函数语句(Alter Function Statement)
ALTER [TEMPORARY|SYSTEM] FUNCTION [IF EXISTS] [catalog_name.][db_name.] function_name RENAME TO new_name;
显示函数语句(Show Function Statement)
SHOW FUNCTION [catalog_name.][db_name]
四、使用场景
我们希望使用function DDL支持尽可能多的使用场景。下面我们列出了一些明显可以实现的场景:
从classpath加载UDF
CREATE TEMPORARY FUNCTION catalog1.db1.func1 AS ‘com.xxx.udf.func1UDF’ LANGUAGE ’JVM’
DROP FUNCTION catalog1.db1.geofence
在本例中,假设UDF class已经在classpath(类路径)中。因此,我们只需要通过反射获取class对象(类对象),以确定它是UDF、UDAF还是UDTF,并将其注册到TableEnvironment中。
从远程资源加载UDF
CREATE FUNCTION catalog1.db1.func2 AS ‘com.xxx.udf.func2UDF’ LANGUAGE JVM USING ‘http://artifactory.uber.internal:4587/artifactory/libs-snapshot-local/com/xxx/xxx/xxx-udf/1.0.1-SNAPSHOT/xxx-udf-1.0.1-20180502.011548-12.jar’
在这种情况下,用户可以使用非本地classpath中的类。在上面的示例中,函数catalog1.db1.func2在一个artifactory的jar中。
使用这种类型的模型,我们可以将用户级逻辑从平台中分离出来。每个团队都可以编写并拥有自己的UDF库。Flink平台只负责将它加载到classpath并使用它。我们将在后面的章节中讨论如何实现它。基本上,resource URL将作为用户库添加到Environment中。它将被添加到JobGraph中,并传送到存储层,例如作业提交之前的HDFS
从远程资源加载python UDF
CREATE FUNCTION catalog1.db1.func3 AS ‘com.xxx.udf.func3UDF’ LANGUAGE ‘PYTHON’ USING ‘http://external.resources/flink-udf.py’
五、新增或更改的公共接口
首先需要做的是,在CatalogFunction接口中添加更多的函数。
public interface CatalogFunction {
String getClassName();
Enum getLanguage(); // TODO
Map<String, String> getProperties();
CatalogFunction copy();
Optional<List<String>> getResourcePaths(); // TODO
Optional<String> getDescription();
Optional<String> getDetailedDescription();
}
第二步:注册用户Jar,为了支持加载外部lib并从外部lib中创建udf,我们需要在ExecutionEnvironment中添加一个函数来注册外部lib。
/**
* Register a jar file to load in the Flink job dynamically. The jar file will be added into job graph before job
* submission. During runtime, the jar file is loaded into user code class loader automatically.
*
* @param jarFile The path of the jar file (e.g., “file://a”, “hdfs://b”, “http://c”)
*/
Public void registerUserJarFile(String jarFile) {
Path path = new Path(jarFile);
this.userJars.add(path)
}
在作业提交之前,注册用户jar将被添加到StreamGraph中,然后被添加到JobGraphGenerator的JobGraph中。
六、资源隔离
为了考虑不同session类加载的隔离性,我们可以在{Stream}ExecutionEnvironment中添加一个新的接口。如:
Public void registerUserJarFiles(String classloaderName, String... jarFiles) {
// ...
}
这个接口可以使用特定key(即classloaderName),注册一组jar文件。在内部,它使用与registerCachedFile()类似的路径,后者使用Flink的Blob服务器将Jar文件分发到runtime运行时。
另外,在RuntimeContext中添加一个新接口,使用在name下注册的Jar文件集,创建并缓存一个自定义userCodeClassLoader。
Public ClassLoader getClassLoaderByName(String classloaderName) {
// ...
}
在UDF function的代码生成(code generation)过程中,它将把多个jar文件加载到自定义ClassLoader中,并使用反射方式调用该函数
另外,在RuntimeContext实现中,我们将保留自定义ClassLoader的缓存,这样我们就不会多次加载同一个lib库。
七、实施计划
注意:对于不同的语言,实现将是不同的,这里我们只讨论java/scala lib所需的API。
从实现的角度来看,我们希望提供与多种语言支持相一致的函数语法,但是目前我们仅讨论java和scala实现范畴。python udf相关的支持将在FLIP-78的范围内进行讨论和实现。
具体的操作项包括:
Flink 1.10 Release
1、在flink-sql-parser中添加function相关语法。
2、在flink-sql-parser模块中定义SqlCreateFunction和SqlDropFunction
3、桥接DDL和TableEnvironment,将function注册到TableEnvironment中
Flink 1.10后续版本
1、支持从java外部资源加载UDF(即支持USING JAR语句)
2、添加scala function相关的支持
FLIP-65作为Table API udf的类型推断接口, 这个FLIP阻碍了向TableEnvImpl中添加scala function。因此,上述1、2、3目前只支持java语言
一旦flip65完成,就可以继Scala function DDL的工作,并将相应的函数注册到TableEnvImpl中。
八、参考
[1] Flink SQL DDL设计
[2] FLIP-69 Flink SQL DDL增强
[3] FLIP-64支持表模块中的临时对象
[4] FLIP-65新类型推理
[5] FLIP-78 Flink Python UDF环境和依赖项管理
九、社区issue和pr进展(总结,非翻译)
1、支持创建、删除、修改函数语法(FLink 1.10.0可用)
FLINK-7151:支持创建、删除、修改函数语法,已提交对应PR GitHub Pull Request #9689
支持的语句
CREATE FUNCTION [IF NOT EXISTS] [catalog_name.db_name.]function_name AS class_name;
DROP FUNCTION [IF EXISTS] [catalog_name.db_name.]function_name;
ALTER FUNCTION [IF EXISTS] [catalog_name.db_name.]function_name RENAME TO new_name;CREATE function ‘TOPK’ AS ‘com.xxxx.aggregate.udaf.distinctUdaf.topk.ITopKUDAF’;
INSERT INTO db_sink SELECT id, TOPK(price, 5, ‘DESC’) FROM kafka_source GROUP BY id;当前issue假定用户已经在classpath中加载了function class。像如何从外部(例如HDFS)动态加载udf库,这样的高级特性在别的issue单独解决。
2、为function DDL添加“USING JAR/FILE/ACHIVE”功能(FLink 1.11.0可用)
FLINK-7151子issue FLINK-14055: 为function DDL添加“USING JAR/FILE/ACHIVE”功能
状态:block and open
3、 在{Stream}ExecutionEnvironment中注册用户jar文件(FLink 1.10.0可用)
FLINK-14319 在{Stream}ExecutionEnvironment中注册用户jar文件
已提交对应PR GitHub Pull Request #9841,投票中。
在{Stream}ExecutionEnvironment中,增加了以下接口:
- void registerUserJarFile(String jarFile)
- void registerUserJarFile(String jarFile)
- void addUserJars(List userJars, JobGraph jobGraph)
- void registerUserJarFile(String jarFile)