debezium关于cdc的使用(下)

本文详细介绍了如何使用Debezium MySQL CDC Connector将数据从MySQL同步到Kafka,包括启动Kafka配套服务,创建Kafka topic,配置Kafka Connect,以及设置Avro序列化的数据流程。此外,还提到了数据变更(DDL和DML)的独立监听器,确保数据库的增删改操作能在目标库中准确反映。
摘要由CSDN通过智能技术生成

博文原址:debezium关于cdc的使用(下)

简介

debezium在debezium关于cdc的使用(上)中有做介绍。具体可以跳到上文查看。本篇主要讲述使用kafka connector方式来同步数据。而kafka connector实际上也有提供其他的sink(Kafka Connect JDBC)来同步数据,但是没有delete事件。所以在这里选择了Debezium MySQL CDC Connector方式来同步。本文需要使用Avro方式序列化kafka数据。

流程

第一步准备

使用kafka消息中间介的话需要对应的服务支持,尤其需要chema-registry来管理schema,因电脑内存有限就没使用docker方式启动,如果条件ok内存够大的话阔以使用docker方式。所以使用的就是local本地方式。具体下载,安装,部署,配置环境变量我就不在重复描述了,阔以参考官方文档。

第二步启动kafka配套

进入目录后启动bin/confluent start

image-20190807100552882

第三步创建kafka topic

可以通过kafka命令创建topic也可以通过Confluent Control Center 地址:http://localhost:9021来创建topic。我们还是按照上文的表来同步数据,所以创建topic:dbserver1.inventory.demo

image-20190807103004882

第四步创建kafka connect

可以通过kafka rest命令创建也可以使用Confluent Control Center创建。

connect的api命令参考

方便点可以使用crul创建,以下为配置文件

{
  "name": "inventory-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "tasks.max": "1",
    "database.hostname": "localhost",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "dbz",
    "database.server.id": "184054",
    "database.server.name": "dbserver1",
    "database.whitelist": "inventory",
    "decimal.handling.mode": "double",
    "key.converter": "io.confluent.connect.avro.AvroConverter",
    "key.converter.schema.registry.url": "http://localhost:8081",
    "value.converter": "io.confluent.connect.avro.AvroConverter",
    "value.converter.schema.registry.url": "http://localhost:8081",
    "database.history.kafka.bootstrap.servers": "localhost:9092",
    "database.history.kafka.topic": "dbhistory.inventory"
  }
}

创建好后可以使用命令查询到或者在管理中心查看。

命令:http://localhost:8083/connectors/inventory-connector

image-20190807105245306

第五步启动同步程序

配置
spring:
  application:
    name: data-center
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/inventory_back?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username: debe
    password: 123456
  jpa:
    show-sql: true
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
#    time-zone: UTC
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: debezium-kafka-connector
      key-deserializer: "io.confluent.kafka.serializers.KafkaAvroDeserializer"
      value-deserializer: "io.confluent.kafka.serializers.KafkaAvroDeserializer"
      properties:
        schema.registry.url: http://localhost:8081
kafka消费者

跟上文的处理流程是一样的。只不过DDL和DML分成2个监听器。

package com.example.kakfa.avro;

import com.example.kakfa.avro.sql.SqlProvider;
import com.example.kakfa.avro.sql.SqlProviderFactory;
import io.debezium.data.Envelope;
import lombok.extern.slf4j.Slf4j;
import org.apache.avro.generic.GenericData;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.Optional;


@Slf4j
@Component
public class KafkaAvroConsumerRunner {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private NamedParameterJdbcTemplate namedTemplate;

    @KafkaListener(id = "dbserver1-ddl-consumer", topics = "dbserver1")
    public void listenerUser(ConsumerRecord<GenericData.Record, GenericData.Record> record) throws Exception {
        GenericData.Record key = record.key();
        GenericData.Record value = record.value();
        log.info("Received record: {}", record);
        log.info("Received record: key {}", key);
        log.info("Received record: value {}", value);

        String databaseName = Optional.ofNullable(value.get("databaseName")).map(Object::toString).orElse(null);
        String ddl = Optional.ofNullable(value.get("ddl")).map(Object::toString).orElse(null);

        if (StringUtils.isBlank(ddl)) {
            return;
        }
        handleDDL(ddl, databaseName);
    }

    /**
     * 执行数据库ddl语句
     *
     * @param ddl
     */
    private void handleDDL(String ddl, String db) {
        log.info("ddl语句 : {}", ddl);
        try {
            if (StringUtils.isNotBlank(db)) {
                ddl = ddl.replace(db + ".", "");
                ddl = ddl.replace("`" + db + "`.", "");
            }

            jdbcTemplate.execute(ddl);
        } catch (Exception e) {
            log.error("数据库操作DDL语句失败,", e);
        }
    }

    @KafkaListener(id = "dbserver1-dml-consumer", topicPattern = "dbserver1.inventory.*")
    public void listenerAvro(ConsumerRecord<GenericData.Record, GenericData.Record> record) throws Exception {
        GenericData.Record key = record.key();
        GenericData.Record value = record.value();
        log.info("Received record: {}", record);
        log.info("Received record: key {}", key);
        log.info("Received record: value {}", value);

        if (Objects.isNull(value)) {
            return;
        }

        GenericData.Record source = (GenericData.Record) value.get("source");
        String table = source.get("table").toString();
        Envelope.Operation operation = Envelope.Operation.forCode(value.get("op").toString());

        String db = source.get("db").toString();

        handleDML(key, value, table, operation);
    }

    private void handleDML(GenericData.Record key, GenericData.Record value,
                           String table, Envelope.Operation operation) {
        SqlProvider provider = SqlProviderFactory.getProvider(operation);
        if (Objects.isNull(provider)) {
            log.error("没有找到sql处理器提供者.");
            return;
        }

        String sql = provider.getSql(key, value, table);
        if (StringUtils.isBlank(sql)) {
            log.error("找不到sql.");
            return;
        }

        try {
            log.info("dml语句 : {}", sql);
            namedTemplate.update(sql, provider.getSqlParameterMap());
        } catch (Exception e) {
            log.error("数据库DML操作失败,", e);
        }
    }

}

数据流程

剩下的就是在inventory库中demo表中增删改数据,在对应的inventory_back库中demo表数据对应的改变。

欢迎关注微信公众号
微信公众号

Flink CDC(Change Data Capture)是一种用于Apache Flink的source connectors,用于从不同的数据库接收更改数据。它使用Debezium作为捕获数据更改的引擎,以实现实时数据流的处理。下面是一个Flink CDC使用案例的示例[^2]: ```java import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer; import java.util.Properties; public class FlinkCDCExample { public static void main(String[] args) throws Exception { // 设置执行环境 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // 设置Kafka连接属性 Properties properties = new Properties(); properties.setProperty("bootstrap.servers", "localhost:9092"); properties.setProperty("group.id", "flink-cdc-example"); // 创建FlinkKafkaConsumer FlinkKafkaConsumer<String> kafkaConsumer = new FlinkKafkaConsumer<>("cdc_topic", new SimpleStringSchema(), properties); // 添加Kafka消费者到执行环境 DataStream<String> stream = env.addSource(kafkaConsumer); // 打印数据流 stream.print(); // 执行任务 env.execute("Flink CDC Example"); } } ``` 上述示例代码演示了如何使用Flink CDC连接器从Kafka主题中消费数据,并将数据流打印出来。你可以根据自己的需求进行进一步的数据处理和分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值