Como aproveitar os plugins de código aberto? Lide com o desenvolvimento de interface de dados de maneira rápida e eficiente e conecte diferentes sistemas de aplicativos

Introdução:

Não é exagero dizer que não há desenvolvedor que ainda não tenha chutado a placa de ferro da "não interoperabilidade de dados de aplicativos" - no caso de diferentes plataformas, diferentes tecnologias, diferentes métodos de armazenamento e implantação e a falta de interfaces, os sistemas de aplicação são diferentes, de difícil comunicação. Com a contínua expansão dos requisitos de negócios, os aplicativos estão em constante desenvolvimento para diversificação e personalização.A contradição entre negócios futuros e pilhas de tecnologia desatualizadas também está se tornando cada vez mais proeminente, e o número de interfaces necessárias também está aumentando.

Como tornar o desenvolvimento da interface simples e rápido tornou-se um problema que precisamos considerar. Recentemente, desenterrei um plug-in de código aberto muito perfumado. Depois de estudar cuidadosamente a documentação técnica, decidi dar a você a Amway:

Plugin de código aberto Tapdata PDK

Link do GitHub: github.com/tapdata/ida…

A editora deste projeto é a Tapdata, uma equipe empresarial nacional especializada em plataformas de serviços de dados em tempo real. Segundo os funcionários, este widget de código aberto também é o trampolim para o código aberto de seus principais produtos. sincronização de dados em tempo real da equipe Uma força bastante madura.

PDK é uma estrutura de desenvolvimento de plug-in de código aberto abstraída de sua tecnologia de interface de dados. Através da interface Source Plugin ou da interface Target Plugin, a adaptação e compatibilidade do novo banco de dados como origem ou destino do Tapdata podem ser realizadas rapidamente, de modo que o produto Tapdata Cloud e o próximo código aberto podem ser realizados rapidamente Tapdata várias fontes de dados heterogêneas para bancos de dados ou plataformas de destino.

De acordo com a especificação de desenvolvimento do conector PDK, o desenvolvimento de fonte e destino de dados pode simplificar o processo de desenvolvimento de link de dados. .

Os tipos de suporte incluem:

  • Banco de dados de acesso: MySQL, Oracle, PostgreSQL, etc.
  • Acesse produtos SaaS : Salesforce, vika table, gold data form, Zoho CRM, etc.
  • Acesso a fontes de dados personalizadas: pode se conectar a fontes de dados de protocolo privado

Acesso rápido ao banco de dados de destino

Atualmente, a equipe do PDK tornou públicos os documentos técnicos e você pode acessar o GitHub ( github.com/tapdata/ida… para obter detalhes.

Prepare o ambiente

  • Java8
  • Especialista
  • Git
  • INtelliJ IDEA

Baixe o código fonte e compile

git clone https://github.com/tapdata/idaas-pdk.git

cd idaas-pdk

mvn clean install
复制代码

Crie um projeto do Connector para o banco de dados de destino

Por exemplo, o grupo é io.tapdata, o nome do banco de dados é XDB e a versão da versão é 0.0.1. Crie um projeto Connector com o seguinte comando

  • Quando o banco de dados de destino não precisa criar uma tabela

./bin/tap template --type target --group io.tapdata --name XDB --version 0.0.1 --output ./connectors
复制代码

Abra o idaas-pdk com o ItelliJ IDEA e você poderá ver o projeto xdb-connector em idaas-pdk/connectors.

  • Preencha configOptions em spec.json

Após o configOptions ser integrado ao site Tapdata, ele configura os itens de entrada para o usuário ao utilizar o Conector, como endereço de conexão do banco de dados, nome de usuário, senha, etc.

{

   ...

   "configOptions":{

      "connection":{

         "type":"object",

         "properties":{

            "host":{

               "type": "string",

               "title": "Host",

               "x-decorator": "FormItem",

               "x-component": "Input"

            }, 

            "port":{

               "type": "number",

               "title": "Port",

               "x-decorator": "FormItem",

               "x-component": "Input"

            }

            

         }

      }

   }

}
复制代码
  • Escreva o código para acessar o banco de dados de destino
@TapConnectorClass("spec.json")

public class XDBConnector extends ConnectorBase implements TapConnector {

 @Override

    public void discoverSchema(TapConnectionContext connectionContext, Consumer<List<TapTable>> consumer) {

        //TODO Load tables from database, connection information in connectionContext#getConnectionConfig

        //Sample code shows how to define tables.

        consumer.accept(list(

                //Define first table

                table("empty-table1"),

                //Define second table

                table("empty-table2"))

        ));

    }


 @Override

    public void connectionTest(TapConnectionContext connectionContext, Consumer<TestItem> consumer) {

        //Assume below tests are successfully, below tests are recommended, but not required.

        //Connection test

        //TODO execute connection test here

        consumer.accept(testItem(TestItem.ITEM_CONNECTION, TestItem.RESULT_SUCCESSFULLY));

        //Login test

        //TODO execute login test here

        consumer.accept(testItem(TestItem.ITEM_LOGIN, TestItem.RESULT_SUCCESSFULLY));

        //Read test

        //TODO execute read test by checking role permission

        consumer.accept(testItem(TestItem.ITEM_READ, TestItem.RESULT_SUCCESSFULLY));

        //Write test

        //TODO execute write test by checking role permission

        consumer.accept(testItem(TestItem.ITEM_WRITE, TestItem.RESULT_SUCCESSFULLY));

    }


 private void writeRecord(TapConnectorContext connectorContext, List<TapRecordEvent> tapRecordEvents, Consumer<WriteListResult<TapRecordEvent>> writeListResultConsumer) {

        //TODO write records into database

        //Below is sample code to print received events which suppose to write to database.

        AtomicLong inserted = new AtomicLong(0); //insert count

        AtomicLong updated = new AtomicLong(0); //update count

        AtomicLong deleted = new AtomicLong(0); //delete count

        for(TapRecordEvent recordEvent : tapRecordEvents) {

            if(recordEvent instanceof TapInsertRecordEvent) {

                //TODO insert record

                inserted.incrementAndGet();

            } else if(recordEvent instanceof TapUpdateRecordEvent) {

                //TODO update record

                updated.incrementAndGet();

            } else if(recordEvent instanceof TapDeleteRecordEvent) {

                //TODO delete record

                deleted.incrementAndGet();

            }

        }

        //Need to tell incremental engine the write result

        writeListResultConsumer.accept(writeListResult()

                .insertedCount(inserted.get())

                .modifiedCount(updated.get())

                .removedCount(deleted.get()));

    }

 private void queryByFilter(TapConnectorContext connectorContext, List<TapFilter> filters, Consumer<List<FilterResult>> listConsumer){

        //Filter is exactly match.

        //If query by the filter, no value is in database, please still create a FitlerResult with null value in it. So that flow engine can understand the filter has no value.

    }

}
复制代码
  • Quando o banco de dados de destino precisa criar uma tabela

./bin/tap template --type targetNeedTable --group io.tapdata --name XDB --version 0.0.1 --output ./connectors
复制代码

Abra o idaas-pdk com o ItelliJ IDEA e você poderá ver o projeto xdb-connector em idaas-pdk/connectors.

  • Preencha configOptions em spec.json

Depois que o configOptions estiver integrado ao site Tapdata, configure os itens de entrada para o usuário ao usar o Conector, como endereço de conexão do banco de dados, nome de usuário, senha, etc.

{
   ...

   "configOptions":{

      "connection":{

         "type":"object",

         "properties":{

            "host":{

               "type": "string",

               "title": "Host",

               "x-decorator": "FormItem",

               "x-component": "Input"

            }, 

            "port":{

               "type": "number",

               "title": "Port",

               "x-decorator": "FormItem",

               "x-component": "Input"

            }     
         }
      }
   }
}
复制代码
  • Preencha dataTypes (expressão de tipo) em spec.json

dataTypes 用于描述该 Connector 接入数据库的所有字段的范围,以及转换到对应的 TapType。 源端数据库也会提供相同的 dataTypes 描述, 这样当源端数据流入到 Tapdata 里时, 会结合源端 dataTypes 的字段描述信息结合源端库表的字段信息, 通过 Tapdata 的中立数据结构进入到 Tapdata 的数据流中, 当数据要流入到目标数据库之前,Tapdata 会根据这些信息, 在目标库的 dataTypes 中找到最佳的存储字段, 通过 TapField 的 originType 告知给 PDK 开发者, 用以建表。

{

   ...

   "dataTypes":{

      "boolean":{"bit":8, "unsigned":"", "to":"TapNumber"},

      "tinyint":{"bit":8, "to":"TapNumber"},

      "smallint":{"bit":16, "to":"TapNumber"},

      "int":{"bit":32, "to":"TapNumber"},

      "bigint":{"bit":64, "to":"TapNumber"},

      "largeint":{"bit":128, "to":"TapNumber"},

      "float":{"bit":32, "to":"TapNumber"},

      "double":{"bit":64, "to":"TapNumber"},

      "decimal[($precision,$scale)]":{"bit": 128, "precision": [1, 27], "defaultPrecision": 10, "scale": [0, 9], "defaultScale": 0, "to": "TapNumber"},

      "date":{"byte":3, "range":["0000-01-01", "9999-12-31"], "to":"TapDate"},

      "datetime":{"byte":8, "range":["0000-01-01 00:00:00","9999-12-31 23:59:59"],"to":"TapDateTime"},

      "char[($byte)]":{"byte":255, "to": "TapString", "defaultByte": 1},

      "varchar[($byte)]":{"byte":"65535", "to":"TapString"},

      "string":{"byte":"2147483643", "to":"TapString"},

      "HLL":{"byte":"16385", "to":"TapNumber", "queryOnly":true}

   }

}
复制代码
  • 编写接入目标数据库的代码
 @TapConnectorClass("spec.json")

public class XDBConnector extends ConnectorBase implements TapConnector {
 @Override

 public void discoverSchema(TapConnectionContext connectionContext, Consumer<List<TapTable>> consumer) {
 //TODO Load schema from database, connection information in connectionContext#getConnectionConfig
 //Sample code shows how to define tables with specified fields
 consumer.accept(list(
 //Define first table
 table("empty-table1")
 //Define a field named "id", origin field type, whether is primary key and primary key position

 .add(field("id", "varchar").isPrimaryKey(true).partitionKeyPos(1))

 .add(field("description", "string"))

 .add(field("name", "varchar"))

 .add(field("age", "int")))

 ));

 }

 @Override

 public void connectionTest(TapConnectionContext connectionContext, Consumer<TestItem> consumer) {

 //Assume below tests are successfully, below tests are recommended, but not required.
 //Connection test
 //TODO execute connection test here
 consumer.accept(testItem(TestItem.ITEM_CONNECTION, TestItem.RESULT_SUCCESSFULLY));
 //Login test

 //TODO execute login test here

 consumer.accept(testItem(TestItem.ITEM_LOGIN, TestItem.RESULT_SUCCESSFULLY));

 //Read test

 //TODO execute read test by checking role permission
 consumer.accept(testItem(TestItem.ITEM_READ, TestItem.RESULT_SUCCESSFULLY));

 //Write test
 //TODO execute write test by checking role permission
 consumer.accept(testItem(TestItem.ITEM_WRITE, TestItem.RESULT_SUCCESSFULLY));

 }

 @Override

 public void registerCapabilities(ConnectorFunctions connectorFunctions, TapCodecRegistry codecRegistry) {

 connectorFunctions.supportWriteRecord(this::writeRecord);
 connectorFunctions.supportQueryByFilter(this::queryByFilter);

 //If database need insert record before table created, then please implement the below two methods.
 connectorFunctions.supportCreateTable(this::createTable);
 connectorFunctions.supportDropTable(this::dropTable);

 //If database need insert record before table created, please implement the custom codec for the TapValue that data types in spec.json didn't cover.

 //TapTimeValue, TapMapValue, TapDateValue, TapArrayValue, TapYearValue, TapNumberValue, TapBooleanValue, TapDateTimeValue, TapBinaryValue, TapRawValue, TapStringValue

 codecRegistry.registerFromTapValue(TapRawValue.class, "text", tapRawValue -> {

 if (tapRawValue != null && tapRawValue.getValue() != null)

 return toJson(tapRawValue.getValue());

 return "null";

 });

 }


 private void writeRecord(TapConnectorContext connectorContext, List<TapRecordEvent> tapRecordEvents, Consumer<WriteListResult<TapRecordEvent>> writeListResultConsumer) {

 //TODO write records into database

 //Below is sample code to print received events which suppose to write to database.

 AtomicLong inserted = new AtomicLong(0); //insert count

 AtomicLong updated = new AtomicLong(0); //update count

 AtomicLong deleted = new AtomicLong(0); //delete count

 for(TapRecordEvent recordEvent : tapRecordEvents) {

 if(recordEvent instanceof TapInsertRecordEvent) {

 //TODO insert record

 inserted.incrementAndGet();

 PDKLogger.info(TAG, "Record Write TapInsertRecordEvent {}", toJson(recordEvent));

 } else if(recordEvent instanceof TapUpdateRecordEvent) {

 //TODO update record

 updated.incrementAndGet();

 PDKLogger.info(TAG, "Record Write TapUpdateRecordEvent {}", toJson(recordEvent));

 } else if(recordEvent instanceof TapDeleteRecordEvent) {

 //TODO delete record

 deleted.incrementAndGet();

 PDKLogger.info(TAG, "Record Write TapDeleteRecordEvent {}", toJson(recordEvent));

 }

 }

 //Need to tell incremental engine the write result

 writeListResultConsumer.accept(writeListResult()

 .insertedCount(inserted.get())

 .modifiedCount(updated.get())

 .removedCount(deleted.get()));

 }

 private void queryByFilter(TapConnectorContext connectorContext, List<TapFilter> filters, Consumer<List<FilterResult>> listConsumer){

 //Filter is exactly match.

 //If query by the filter, no value is in database, please still create a FitlerResult with null value in it. So that flow engine can understand the filter has no value.

 }

 private void dropTable(TapConnectorContext connectorContext, TapDropTableEvent dropTableEvent) {

 TapTable table = connectorContext.getTable();

 //TODO implement drop table

 }

 private void createTable(TapConnectorContext connectorContext, TapCreateTableEvent createTableEvent) {

 //TODO implement create table.

 TapTable table = connectorContext.getTable();

 LinkedHashMap<String, TapField> nameFieldMap = table.getNameFieldMap();

 for(Map.Entry<String, TapField> entry : nameFieldMap.entrySet()) {

 TapField field = entry.getValue();

 String originType = field.getOriginType();

 //originType is the data types defined in spec.json

 //TODO use the generated originType to create table.

 }

 }

 }
复制代码

开发完成之后通过 TDD 进行测试验证

提供 configOptions 里需要用户填写的内容的 json 文件, 例如上述 configOptions 里要求用户填写的是数据库的 Host 和 Port, 那么 tdd 的 xdb_tdd.json 文件内容如下:

{
    "connection": {

      "host": "192.168.153.132",

      "port": 9030,

    }
}
复制代码

执行 TDD 测试命令:

./bin/tap tdd --testConfig xdb_tdd.json ./connectors/xdb-connector
复制代码

当 TDD 测试没有通过, 请根据错误提示修改对应错误,直至通过 TDD测试;

当 TDD 测试通过后, PDK Connector 就处于可以提交 Pull Request 的状态。

如何提交到 PDK 开源项目

① fork idaas-pdk, 基于远程的 main 分支建立本地分支

② 根据要接入数据库名称, 在 idaas-pdk/connectors 目录下新建模块, 命名规范为 {数据库小写名称}-connector, 例如接入数据库的名称为 XDB, 模块名称为 xdb-connector

③ 开发者根据官方 API 文档完成接入数据库的开发实现

④ 通过 TDD 测试后, 提交 PR 到 idaas-pdk

⑤ 官方团队 Review 提交的 PR 之后合并代码

彩蛋

Alunos interessados, não se apressem em desenvolvê-lo. Entende-se que a versão oficial gratuita realizou sucessivamente conexão de dados em tempo real entre 30 fontes/destinos de dados comuns. Se já contém o banco de dados que deseja acessar, você pode usar diretamente Tapdata Cloud ( tapdata.net/tapdata-clo… Tapdata PDK autodesenvolvimento, acesso rápido.

Tipos de conexão de dados atualmente suportados pelo Tapdata Cloud

Atualmente, a Tapdata abriu um grupo de co-construção ecológica de plug-ins para desenvolvedores. Ele pode fornecer trocas técnicas e suporte durante o processo de desenvolvimento. Os alunos interessados ​​podem digitalizar o código a seguir e convidá-lo a participar do grupo:

Acho que você gosta

Origin juejin.im/post/7085896306475925534
Recomendado
Clasificación