栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Flink CDC2.2动态加载表,读取mysql数据,写入到mq

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Flink CDC2.2动态加载表,读取mysql数据,写入到mq

1、使用 Flink Stream API 开发一个 Flink CDC Demo

2、以Mysql为例,采集Mysql binlog数据并发送到mq

3、生成 checkpoint 数据,重启程序从执行的状态恢复数据

4、演示2.2版本动态加载表的新特性

新建一个maven项目,引入pom



    com.ververica
    flink-connector-mysql-cdc
    2.2.1


    org.apache.flink
    flink-streaming-java_2.12
    1.13.6


    org.apache.flink
    flink-clients_2.12
    1.13.6


    org.apache.flink
    flink-java
    1.13.6


    org.apache.flink
    flink-table-planner-blink_2.12
    1.13.6
    test-jar



    org.springframework.boot
    spring-boot-starter-amqp
    2.0.9.RELEASE


    com.alibaba
    fastjson
    1.2.75

新建mq工厂类

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

import java.io.IOException;
import java.util.concurrent.TimeoutException;


public class HandlerSink extends RichSinkFunction {

    private transient Channel channel;
    private transient Connection connection;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        // create connection factory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setConnectionTimeout(0);
        connection = connectionFactory.newConnection();
        channel = connection.createChannel();
        channel.queueDeclare("direct.cdc.data.queue", true, false, false, null);

    }

    @Override
    public void close() throws IOException, TimeoutException {
        //关闭资源
        channel.close();
        connection.close();
    }

    @Override
    public void invoke(String value, Context context) throws Exception {
        if(StringUtils.isNotBlank(value)){
            channel.basicPublish("direct.cdc.data.sync", "flink-cdc-sync-data", null, value.getBytes());
        }
    }

}

cdc解析日志数据

import com.alibaba.fastjson.JSONObject;
import com.ververica.cdc.debezium.DebeziumDeserializationSchema;
import io.debezium.data.Envelope;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.util.Collector;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.source.SourceRecord;

import java.util.List;


public class MyJsonDebeziumDeserializationSchema implements DebeziumDeserializationSchema {

    @Override
    public void deserialize(SourceRecord sourceRecord, Collector collector)
            throws Exception {
        Struct value = (Struct) sourceRecord.value();
        Struct source = value.getStruct("source");

        // get db
        String db = source.getString("db");
        String table = source.getString("table");

        // get type, create and read convert to insert
        String type = Envelope.operationFor(sourceRecord).toString().toLowerCase();
        if ("create".equals(type) || "read".equals(type)) {
            type = "insert";
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("database", db);
        jsonObject.put("table", table);
        jsonObject.put("type", type);
        // get data
        Struct before = value.getStruct("before");
        Struct after = value.getStruct("after");
        JSONObject dataJson = new JSONObject();
        List fields;
        // delete get before else get after
        if("delete".equals(type)){
            fields = before.schema().fields();
        }else{
            fields = after.schema().fields();
        }
        for (Field field : fields) {
            String field_name = field.name();
            Object fieldValue;
            if("delete".equals(type)){
                fieldValue  = before.get(field);
            }else{
                fieldValue  = after.get(field);
            }

            dataJson.put(field_name, fieldValue);
        }
        
        jsonObject.put("data", JSONObject.toJSONString(dataJson));
        collector.collect(JSONObject.toJSONString(jsonObject));
    }

    @Override
    public TypeInformation getProducedType() {
        return BasicTypeInfo.STRING_TYPE_INFO;
    }

main方法直接配置数据库和表结构

import com.hui.www.service.flinkcdc.HandlerSink;
import com.hui.www.service.flinkcdc.MyJsonDebeziumDeserializationSchema;
import com.ververica.cdc.connectors.mysql.source.MySqlSource;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;


public class WebApplication {
    public static void main(String[] args) {
        try {
          	// config env and db
            MySqlSource cdcMysqlSource = MySqlSource.builder()
                    .hostname("127.0.0.1")
                    .port(3306)
                    .scanNewlyAddedTableEnabled(true)
                    .username("user")
                    .password("user123")
              			.serverTimeZone("Asia/Shanghai")
                    .databaseList("kd_shop_1,kd_main")
                    .tableList("kd_shop_1.kd_goods, kd_shop_1.kd_goods_pool" +
                            "kd_main.kd_shop")
                    .deserializer(new MyJsonDebeziumDeserializationSchema())
                    .build();

            Configuration configuration = new Configuration();
            // read checkpoint record
          	// 第一次读取需要注释此行,后续增加表时,开启此行,flink-ck后 ‘27b27e36750ff997a7bd3b9933c5f3c9/chk-12404’换成存储路径下对应文件夹即可,实现旧表增量读取,新表全量读取
            configuration.setString("execution.savepoint.path", "file:///tmp/flink-ck/27b27e36750ff997a7bd3b9933c5f3c9/chk-12404");
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(configuration);
            // enable checkpoint
            env.enableCheckpointing(3000);
            // set local storage path
            env.getCheckpointConfig().setCheckpointStorage("file:///tmp/flink-ck");
           	// output mq
            env.fromSource(cdcMysqlSource, WatermarkStrategy.noWatermarks(), "MySQL Source")
                    .addSink(new HandlerSink());
            env.execute("flinkCdc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接收实体

public class CdcDataSyncDO {
    String database;
    String type;
    String table;
    String data;
}

mq接收类

@Slf4j
@Component
public class CdcDataSyncListener implements BaseRabbitHandler {

    @Autowired
    private SyncDataHandlerProcessService syncDataHandlerProcessService;

    @RabbitHandler
    @RabbitListener(queues = {RabbitConfig.QUEUE_CDC_DATA_SYNC}, containerFactory = "multiListenerContainer")
    public void handler(@Payload byte[] param, Message message, Channel channel) {
        String paramStr = new String(param, StandardCharsets.UTF_8);
        log.info("cdc同步数据结束-{}", Util.toJson(paramStr));
        CdcDataSyncDO cdcDataSyncDO = JSON.parseObject(paramStr, CdcDataSyncDO.class);
        // 根据表名返回对应的接口实现类,策略模式
        SyncDataHandlerService handler = syncDataHandlerProcessService.getHandler(cdcDataSyncDO.getTable());
        handler.syncData(cdcDataSyncDO);
        log.info("cdc同步数据结束-{}", Util.toJson(paramStr));
    }

    @Override
    public void executeException(Object[] datas, Throwable throwable) {
        log.error("cdc同步数据失败,失败原因 : " + throwable.getMessage());
    }
}

策略类分发

@Service
@Data
public class SyncDataHandlerProcessService {
    @Autowired
    GoodsPublishSyncDataImpl goodsPublishSyncData;

    @Autowired
    GoodsPoolSyncDataImpl goodsPoolSyncData;
    @Autowired
    CollectErrorMessageSyncDataImpl collectErrorMessageSyncData;

    
    public SyncDataHandlerService getHandler(String tableName){
        if(tableName.equals(CdcSyncDataConstant.GOODS_PUBLISH_ALL)){
            return goodsPublishSyncData;
        }else if(tableName.equals(CdcSyncDataConstant.GOODS_PUBLISH_POOL)){
            return goodsPoolSyncData;
        }else if (tableName.equals(CdcSyncDataConstant.COLLECT_ERROR_MESSAGE)){
            return collectErrorMessageSyncData;
        } else if (CdcSyncDataConstant.SHOP.equals(tableName)) {

        } else if (CdcSyncDataConstant.SHOP_ACCOUNT.equals(tableName)) {

        } else if (CdcSyncDataConstant.SHOP_AUTH_INFO.equals(tableName)) {

        }

        return null;
    }


    
    public void refreshData(CdcRefreshPO param){
        SyncDataHandlerService handler = getHandler(param.getTableName());
        handler.refreshData(param.getStartTime(),param.getEndTime());
    }
}

接口类进行sql类型解析

public interface SyncDataHandlerService {
    
    default void syncData(CdcDataSyncDO param) {
        if ("delete".equals(param.getType())) {
            deleteSync(param);
        } else if ("insert".equals(param.getType())) {
            insertSync(param);
        } else if ("update".equals(param.getType())) {
            updateSync(param);
        } else {
            return;
        }
    }


    
    String deleteSync(CdcDataSyncDO param);

    
    String updateSync(CdcDataSyncDO param);

    
    String insertSync(CdcDataSyncDO param);
}

总结:

1、通过cdc读取mysql binlog日志,然后进行解析封装,发送到mq;

2、mq接收消息根据不同策略,读取不同表数据,进行crud,并写入es或其他地方;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/988886.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号