基于Canal与Flink实现数据实时增量同步(二)

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

**开源地址:https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB **

import java.util.Map;

import java.util.Properties;

/**

*  @Created with IntelliJ IDEA.

*  @author : jmx

*  @Date: 2020/3/27

*  @Time: 12:52

*

*/

public class HdfsSink {

public static void main(String[] args) throws Exception {

String fieldDelimiter = “,”;

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

env.setParallelism(1);

// checkpoint

env.enableCheckpointing(10_000);

//env.setStateBackend((StateBackend) new FsStateBackend(“file:///E://checkpoint”));

env.setStateBackend((StateBackend) new FsStateBackend(“hdfs://kms-1:8020/checkpoint”));

CheckpointConfig config = env.getCheckpointConfig();

config.enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION);

// source

Properties props = new Properties();

props.setProperty(“bootstrap.servers”, “kms-2:9092,kms-3:9092,kms-4:9092”);

// only required for Kafka 0.8

props.setProperty(“zookeeper.connect”, “kms-2:2181,kms-3:2181,kms-4:2181”);

props.setProperty(“group.id”, “test123”);

FlinkKafkaConsumer consumer = new FlinkKafkaConsumer<>(

“qfbap_ods.code_city”, new SimpleStringSchema(), props);

consumer.setStartFromEarliest();

DataStream stream = env.addSource(consumer);

// transform

SingleOutputStreamOperator cityDS = stream

.filter(new FilterFunction() {

// 过滤掉DDL操作

@Override

public boolean filter(String jsonVal) throws Exception {

JSONObject record = JSON.parseObject(jsonVal, Feature.OrderedField);

return record.getString(“isDdl”).equals(“false”);

}

})

.map(new MapFunction<String, String>() {

@Override

public String map(String value) throws Exception {

StringBuilder fieldsBuilder = new StringBuilder();

// 解析JSON数据

JSONObject record = JSON.parseObject(value, Feature.OrderedField);

// 获取最新的字段值

JSONArray data = record.getJSONArray(“data”);

// 遍历,字段值的JSON数组,只有一个元素

for (int i = 0; i < data.size(); i++) {

// 获取到JSON数组的第i个元素

JSONObject obj = data.getJSONObject(i);

if (obj != null) {

fieldsBuilder.append(record.getLong(“id”)); // 序号id

fieldsBuilder.append(fieldDelimiter); // 字段分隔符

fieldsBuilder.append(record.getLong(“es”)); //业务时间戳

fieldsBuilder.append(fieldDelimiter);

fieldsBuilder.append(record.getLong(“ts”)); // 日志时间戳

fieldsBuilder.append(fieldDelimiter);

fieldsBuilder.append(record.getString(“type”)); // 操作类型

for (Map.Entry<String, Object> entry : obj.entrySet()) {

fieldsBuilder.append(fieldDelimiter);

fieldsBuilder.append(entry.getValue()); // 表字段数据

}

}

}

return fieldsBuilder.toString();

}

});

//cityDS.print();

//stream.print();

// sink

// 以下条件满足其中之一就会滚动生成新的文件

RollingPolicy<String, String> rollingPolicy = DefaultRollingPolicy.create()

.withRolloverInterval(60L * 1000L) //滚动写入新文件的时间,默认60s。根据具体情况调节

.withMaxPartSize(1024 * 1024 * 128L) //设置每个文件的最大大小 ,默认是128M,这里设置为128M

.withInactivityInterval(60L * 1000L) //默认60秒,未写入数据处于不活跃状态超时会滚动新文件

.build();

StreamingFileSink sink = StreamingFileSink

//.forRowFormat(new Path(“file:///E://binlog_db/city”), new SimpleStringEncoder())

.forRowFormat(new Path(“hdfs://kms-1:8020/binlog_db/code_city_delta”), new SimpleStringEncoder())

.withBucketAssigner(new EventTimeBucketAssigner())

.withRollingPolicy(rollingPolicy)

.withBucketCheckInterval(1000)  // 桶检查间隔,这里设置1S

.build();

cityDS.addSink(sink);

env.execute();

}

}

对于Flink Sink到HDFS,StreamingFileSink 替代了先前的 BucketingSink,用来将上游数据存储到 HDFS 的不同目录中。它的核心逻辑是分桶,默认的分桶方式是 DateTimeBucketAssigner,即按照处理时间分桶。处理时间指的是消息到达 Flink 程序的时间,这点并不符合我们的需求。因此,我们需要自己编写代码将事件时间从消息体中解析出来,按规则生成分桶的名称,具体代码如下:

package com.etl.kafka2hdfs;

import org.apache.flink.core.io.SimpleVersionedSerializer;

import org.apache.flink.streaming.api.functions.sink.filesystem.BucketAssigner;

import org.apache.flink.streaming.api.functions.sink.filesystem.bucketassigners.SimpleVersionedStringSerializer;

import java.text.SimpleDateFormat;

import java.util.Date;

/**

*  @Created with IntelliJ IDEA.

*  @author : jmx

*  @Date: 2020/3/27

*  @Time: 12:49

*

*/

public class EventTimeBucketAssigner implements BucketAssigner<String, String> {

@Override

public String getBucketId(String element, Context context) {

String partitionValue;

try {

partitionValue = getPartitionValue(element);

} catch (Exception e) {

partitionValue = “00000000”;

}

return “dt=” + partitionValue;//分区目录名称

}

@Override

public SimpleVersionedSerializer getSerializer() {

return SimpleVersionedStringSerializer.INSTANCE;

}

private String getPartitionValue(String element) throws Exception {

// 取出最后拼接字符串的es字段值,该值为业务时间

long eventTime = Long.parseLong(element.split(“,”)[1]);

Date eventDate = new Date(eventTime);

return new SimpleDateFormat(“yyyyMMdd”).format(eventDate);

}

}

离线还原MySQL数据

经过上述步骤,即可将Binlog日志记录写入到HDFS的对应的分区中,接下来就需要根据增量的数据和存量的数据还原最新的数据。Hive 表保存在 HDFS 上,该文件系统不支持修改,因此我们需要一些额外工作来写入数据变更。常用的方式包括:JOIN、Hive 事务、或改用 HBase、kudu。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值