https://blog.csdn.net/ufolr/article/details/109749564

    OpenLooKeng Connector 扩展 一、什么是Connector
    我们知道在整个Presto工程中所有的功能都是以插件的形式实现,而Connector则是一种负责Presto与数据源进行交互的插件,不同的数据库对应于不同的Connector。

    SPI(Service Provider Interface)
    SPI是JDK内置的服务提供/发现机制,它通过在ClassPath路径下的META-INF/services/目录中定义的文件,自动加载文件里所定义的类。该机制为很多框架的扩展提供了可能,如知名的JDBC就使用了该机制。

    SPI特点:

    SPI文件名为实现Service接口的全限定名
    SPI文件内容为实现该接口的具体文件
    使用ServiceLoader.load(Class class)动态加载Service接口的实现类
    如果SPI实现的类在外部Jar包中,则需要将该jar包放在当前程序的classpath下
    Service的实现类必须有无参构造方法
    PrestoPlugin(插件)
    OpenLookeng中有一个presto-spi模块,该模块即定义了OpenLooKeng对外暴露的SPI接口,实现对应的接口即可实现连接器、类型、函数、系统访问控制等的功能。

    特别的,OpenLooKeng的插件即实现了Plugin SPI的模块:

    io.prestosql.spi.Plugin
    1
    通过在META-INF/services/io.prestosql.spi.Plugin文件中列出实现io.prestosql.spi.Plugin接口的具体类,即可使该实现通过java内置的ServiceLoader提供给OpenLooKeng。对于包含在OpenLooKeng源码中的插件,只要在pom.xml中包含presto-plugin就会自动创建spi文件。

    Connector
    Connector即连接器,是一类用于在OpenLookeng中提供查询特定数据源能力的插件。即使OpenLooKeng尚未支持当前数据源,只要将数据源与OpenLooKeng期望使用的API适配,创建一个Connector,即可扩展对该数据源的支持。

    Connector实例由ConnectorFactory实例创建,OpenLooKeng调用插件上的getConnectorFactory()时会创建该ConnectorFactory实例。
    ConnectorFactory可以返回一下实例:

    ConnectorMetadata 元数据接口,允许OpenLooKeng查看模式列表、表列表、列列表等元数据信息。如果要接入的数据源是非关系型数据库,则需要将数据源映射到OpenLooKeng的 模式、表、列概念。
    ConnectorSplitManager分片管理器,将表的数据分区为多块,将块由OpenLooKeng分发到不同的工作节点处理。对于没有已分区数据的数据源,比较好的策略是针对整张表返回单个分片。
    ConnectorHandleResolver
    ConnectorRecordSetProvider 记录集提供器,给定一个分片和一个列列表的情况下,记录集提供器将数据提供给OpenLooKeng执行引擎。记录集提供器创建一个RecordSet,RecordSet又相应的创建一个RecordCursor,OpenLooKeng使用该RecordCursor来读取每一行的值。
    二、Connector 结构


    三、Connector 开发
    Maven工程配置
    在OpenLooKeng源码根目录创建与Connector名对应的目录,并在该目录中创建pom.xml
    将当前模块加入io.hetu.core组中,版本需要和当前OpenLooKeng工程版本一致:

    presto-root
    io.hetu.core
    316

    1
    2
    3
    4
    5
    定义当前工程信息,添加packaging选项为hetu-plugin在打包编译时会将当前工程打包到heto-core的plugin目录下。
    hetu-example
    ${dep.hetu.version}
    Hetu - Example Connector
    hetu-plugin
    1
    2
    3
    4
    引入SPI依赖,每个Plugin工程都会依赖presto-spi模块:

    io.hetu.core
    presto-spi
    provided

    1
    2
    3
    4
    5
    在整个编译整个OpenLooKeng工程时,我们需要将自定义添加的connector也编译打包到plugin目录下,通过以下的配置即可达到我们的目的:
    在hetu core根目录下的pom.xml中,将我们新增的Connector加入到modules中。

    ……
    hetu-example
    ……

    1
    2
    3
    4
    5
    在hetu-server的src/main/provisio/hetu.xml配置文件中,注册新增的Connector。





    1
    2
    3
    4
    5
    经过以上步骤,新增Connector的准备工作就已经完成。在开发过程中根据具体实现使用到的类来添加依赖。Plugin使用了独立的类加载器,和其他的类是隔离的,因此Plugin可以使用不同版本的类库,区别于hetu core内部使用的版本。

    Connector的加载由ConnectorFactory实现。但真正能够使用该Connector还需要加载Catalog,所谓Catalog就是Hetu的数据源类别,Hetu通过如下三级结构来定义数据表。


    非JDBC数据源
    对于非JDBC数据源,我们需要根据数据源的情况,手动实现一个完整Connector的几个必须组件。

    接口名 作用
    ConnectorFactory Connector工厂
    ConnectorMetadata 获取数据源元数据
    ConnectorHandleResolver 获取各种Handler
    ConnectorSplitManager 处理任务分片
    ConnectorRecordSetProvider
    ConnectorPageSourceProvider 读取数据
    ConnectorPageSinkProvider 写入数据
    其调用逻辑如下图:


    Metadata

    实现io.prestosql.spi.connector.ConnectorMetadata接口,提供了对数据源元数据的管理,例如列举Schemas、Tables、Columns信息,创建表、删除表,创建视图、删除视图等一系列DDL操作。

    SplitManager

    Split详细描述了一个数据分片的具体内容,每种数据源的Split都不尽相同,数据源中定义的Split即为Source Stage中调度的Split。Split的分片合理性直接决定了Hetu读取数据源数据的效率。如果一个Split分片涵盖的数据量过大,数据不均匀,则会拖慢整个SQL;若过小,则会造成大量的CPU资源都耗费在调度Split的操作上。

    Splits通过SplitManager获取,最终是通过接口io.prestosql.spi.connector.ConnectorSplitManager的实现类,也就是各个Connector的SplitManager获取Split,该接口包括了以下方法用于获取Split:

    ConnectorSplitSource getSplits();
    1
    ConnectorSplitSource为一个Connector中具体表的Split集合,主要包含以下几个方法:

    //批量获取Splits列表,maxSize默认为1000,Split如何组织以及每调用一次该方法如何返回ConnectorSplitBatch都由各个Connector定义
    CompletableFuture getNextBatch(ConnectorPartitionHandle partitionHandle, int maxSize);

    //Split列表是否获取完毕
    boolean isFinished();
    1
    2
    3
    4
    5
    RecordSetProvider

    获取到Source Split后,将其调度到具体的Worker节点上执行数据读取,数据读取过程需要依赖Split对应的Connector所提供的数据读取类,也就是RecordSetProvider。

    数据源的RecordSetProvider均需要实现io.prestosql.spi.connector.ConnectorRecordSetProvider接口,接口定义也很简单,提供了输入参数是Split、表和表的列信息及输出为RecordSet的方法。

    RecordSet getRecordSet(
    ConnectorTransactionHandle transaction,
    ConnectorSession session,
    ConnectorSplit split,
    ConnectorTableHandle table,
    List<? extends ColumnHandle> columns);
    1
    2
    3
    4
    5
    6
    其中,RecordSet为io.prestosql.spi.connector.RecordSet,其定义的方法有:

    List getColumnTypes();
    RecordCursor cursor();
    1
    2
    cursor方法返回的RecordCursor就是真正的读取数据类,其定义了Split的数据读取方法,针对每个Split都会创建一个RecordCursor实例.

    JDBC 数据源
    什么是JDBC
    JDBC(Java Database Connectivity),即Java数据库编程接口,是Java语言访问数据库的一种规范,是一套API。

    JDBC API主要位于JDK中的java.sql包之中(JavaEE扩展内容位于javax.sql包中),数据库的JDBC驱动即针对该数据库的JDBC API接口实现类,任意数据库厂商或者个人都可以根据JDBC规范实现自己的驱动。

    通过使用JDBC,JAVA开发人员可以使用相同的代码查询几乎任意一种数据库,不必考虑数据库方言。

    JDBC Connector 原理
    由JDBC API的定义可知,理论上我们可以通过相同的Java代码去查询任意提供了JDBC驱动的数据库。

    所以我们可以基于JDBC规范在OpenLooKeng中实现一套统一的、适配OpenLookeng Connector的数据库操作逻辑,当我们在面对不同JDBC数据源时,只需要替换JDBC驱动即可以对该数据源进行操作。

    JDBC Connector 开发
    基于上述原理,OpenLooKeng提供了JDBC Connector的适配代码——JdbcPlugin,开发JDBC 数据源的Connector,最理想的状态只需要引入对应数据源的驱动即可。

    具体实现为:使用需要的Jdbc驱动实例构建ConnectionFactory即可。

    但是!

    不同的数据源各自有各自的特殊情况,对于JDBC的支持和实现细节都可能会有些许差异,所以应对不同的数据源,我们需要在了解该数据源特性和JDBC驱动使用的基础上,对原始JDBC进行一些适配性微调。

    这些适配性调整的实现也很简单,只需要我们继承JdbcPlugin中对应的类型实现自己的处理类型,重载需要调整的方法即可。

    通常需要调整的类型主要有:

    JdbcPlugin 插件入口
    JdbcMetadata 元数据管理类
    BaseJdbcClient 数据库交互类
    QueryBuilder Query处理类