Flinkx實時和離線同步Postgresql資料到Kafka

奔波兒灞0.0發表於2020-12-02

Flinkx實時和離線同步Postgresql資料到Kafka

一、環境部署

1.選擇一臺伺服器,安裝了Flink的更好,使用git工具把專案clone到本地

git clone https://github.com/DTStack/flinkx.git
cd flinkx

或者直接下載原始碼

wget https://github.com/DTStack/flinkx/archive/1.8.5.zip
unzip flinkx-1.8.5.zip
cd flink-1.8.5

2.編譯外掛

mvn clean package -DskipTests

編譯需要下載很多依賴,這邊比較慢,如果編譯過程中報錯 找不到DB2、達夢、gbase、ojdbc8等驅動包,這個時候可以使用內部提供的指令碼進行安裝 ./install_jars.sh ,指令碼在bin目錄下面。然後再重新編譯一下就可以了。

離線同步

首先讀取編寫一個比較簡單的任務,讀取pg庫資料,直接列印到控制檯。

任務json檔案:

{
  "job": {
    "content": [{
		"reader": {
        "parameter" : {
          "column" : [ {
            "name" : "id",
            "type" : "bigint",
            "key" : "id"
          } ],
          "username" : "賬號",
          "password" : "密碼",
          "connection" : [ {
            "jdbcUrl" : [ "jdbc連線地址" ],
            "table" : [ "yrw_test" ]
          } ],
          "where": "id > 0",
          "splitPk": "id",
          "fetchSize": 1000,
          "queryTimeOut": 1000,
          "customSql": "",
          "requestAccumulatorInterval": 2
        },
        "name" : "postgresqlreader"
      },
      "writer": {
        "name": "streamwriter",
        "parameter": {
          "print": true
        }
      }
    }],
    "setting": {
      "speed": {
        "channel": 1,
        "bytes": 0
      },
      "errorLimit": {
        "record": 100
      }
    }
  }
}

執行命令:

/data/bigdata/module/flinkx/flinkx_1.10/bin/flinkx -mode local \
                 -job /data/bigdata/module/flinkx/job/pg2print.json \
       -pluginRoot /data/bigdata/module/flinkx/flinkx_1.10/plugins \
       -confProp "{\"flink.checkpoint.interval\":60000}"

這樣就可以執行離線同步pg的任務啦,這是本地模式,當然Flinkx還提供了 standlone和yarn模式,可以參考官網的方式即可。

實時同步

這邊演示通過cdc的方式實時同步pg庫的資料,然後寫入到kafka中。

任務json檔案:

{
  "job": {
    "content": [{
      "reader" : {
        "parameter" : {
          "username" : "賬號",
          "password" : "密碼",
          "url" : "pg連線url",
          "databaseName" : "資料庫名",
          "cat" : "update,insert,delete",
          "tableList" : [
            "表名"
          ],
          "statusInterval" : 10000,
          "lsn" : 0,
          "slotName" : "aaa",
          "allowCreateSlot" : true,
          "temporary" : true,
          "pavingData" : true
        },
        "name" : "pgwalreader"
      },
       "writer" : {
        "parameter": {
          "timezone": "UTC",
          "topic": "yrw_test_flinkx",
          "producerSettings": {
            "zookeeper.connect" : "zookeeper的地址",
            "bootstrap.servers" : "kafka的bootstrap.servers"
        }
        },
        "name": "kafka10writer"
      }
    } ],
    "setting": {
      "speed": {
        "channel": 1,
        "bytes": 0
      },
      "errorLimit": {
  		"record": 100
      },
      "restore": {
        "maxRowNumForCheckpoint": 0,
        "isRestore": false,
        "isStream" : true,
        "restoreColumnName": "",
        "restoreColumnIndex": 0
      },
      "log" : {
        "isLogger": false,
        "level" : "debug",
        "path" : "",
        "pattern":""
      }

    }
  }
}

執行命令

/data/bigdata/module/flinkx/flinkx_1.10/bin/flinkx -mode local \
                     -job /data/bigdata/module/flinkx/job/pg_wal_kafka.json \
           -pluginRoot /data/bigdata/module/flinkx/flinkx_1.10/plugins \
           -confProp "{\"flink.checkpoint.interval\":60000}" \
          -jobid yrw_test_1125

執行後會發現任務出錯,需要修改原始碼

com.dtstack.flinkx.pgwal.format.PgWalInputFormat#openInputFormat

    @Override
    public void openInputFormat() throws IOException{
        super.openInputFormat();//增加這一行
        executor = Executors.newFixedThreadPool(1);
        queue = new SynchronousQueue<>(true);
    }

com.dtstack.flinkx.pgwal.reader.PgwalReader#readData

@Override
    public DataStream<Row> readData() {
        PgWalInputFormatBuilder builder = new PgWalInputFormatBuilder();
        builder.setUsername(username);
        builder.setPassword(password);
        builder.setUrl(url);
        builder.setDatabaseName(databaseName);
        builder.setCat(cat);
        builder.setPavingData(pavingData);
        builder.setTableList(tableList);
        builder.setRestoreConfig(restoreConfig);
        builder.setStatusInterval(statusInterval);
        builder.setLsn(lsn);
        builder.setSlotName(slotName);
        builder.setAllowCreateSlot(allowCreateSlot);
        builder.setTemporary(temporary);
        builder.setDataTransferConfig(dataTransferConfig);//增加這一行
        return createInput(builder.finish(), "pgwalreader");
    }

重新打包編譯即可。

在使用kafka10writer時,會存在一些問題,比如說我當前同步到了pg的資料到kafka中,這個時候kafka有5個分割槽,我如何保證同一個id的操作傳送到同一個分割槽呢,因為只有這樣才能保證資料的一致性。這個時候可以指定id作為record的key,使得同一條記錄的操作傳送到同一個分割槽。

修改com.dtstack.flinkx.kafka10.writer.Kafka10OutputFormat

    @Override
    protected void emit(Map event) throws IOException {
        String tp = Formatter.format(event, topic, timezone);
        String key = (String) (event.get("before_id") == null? event.get("after_id"):event.get("before_id"));//將主鍵設定為record的key
        producer.send(new ProducerRecord<>(tp, /*event.toString()*/  key, objectMapper.writeValueAsString(event)), (metadata, exception) -> {
            if(Objects.nonNull(exception)){
                String errorMessage = String.format("send data failed,data 【%s】 ,error info  %s",event,ExceptionUtil.getErrorMessage(exception));
                LOG.warn(errorMessage);
                throw new RuntimeException(errorMessage);
            }
        });
    }

打包重新編譯一下 就ok了。

最後,奉上Flinkx的git連結https://github.com/DTStack/flinkx

相關文章