canal将binlog直接同步到kafka

1. 说明

canal只需配置一个kafka的example实例,就可以直接将binlog同步到kafka, 此时kafka相当于一个id为1001的client

各组件说明如下表:

组件版本服务器安装教程
canal1.1.5canal1, canal2, canal3canal HA安装 + mysql多节点自动切换
kafka2.8.0canal1, canal2, canal3kafka全分布式安装
mysql8.0.25canal1, canal2mysql master-master架构搭建
zookeeper3.6.3canal1, canal2, canal3zookeeper全分布式安装

2. 配置修改

2.1 conf/canal.properties修改

# 修改部分
# 此参数不能在example/instance.properties设置;开启此模式, 参数canal.port = 11111无效
canal.serverMode = kafka

kafka.bootstrap.servers = canal1:9092,canal2:9092,canal3:9092
canal.mq.flatMessage = false

# 添加部分
canal.mq.transaction = false

2.2 新建一个example_kafka实例

先拷贝一个,用于修改

[root@canal1 ~]# 
[root@canal1 ~]# pwd
/root
[root@canal1 ~]# 
[root@canal1 ~]# cp -r canal.deployer-1.1.5/conf/example_db1 example_kafka
[root@canal1 ~]# 
[root@canal1 ~]# ls | grep example_kafka
example_kafka
[root@canal1 ~]#
[root@canal1 ~]# rm example_kafka/h2.mv.db -f
[root@canal1 ~]#
[root@canal1 ~]# ll example_kafka/
总用量 4
-rwxr-xr-x. 1 root root 2680 6月  27 17:29 instance.properties
[root@canal1 ~]# 

修改example_kafka/instance.properties

因为我们下面kafka使用了3个partition, canal在kafka自动创建topic默认是1个partition, 所以我们这里修改kafka_2.13-2.8.0/config/server.properties的参数num.partitions=3,然后重启kafka

[root@canal1 ~]# pwd
/root
[root@canal1 ~]# 
[root@canal1 ~]# cat example_kafka/instance.properties 
#################################################
## mysql serverId , v1.0.26+ will autoGen
# canal2为1252,canal3为1253
canal.instance.mysql.slaveId=1251

# enable gtid use true/false
canal.instance.gtidon=false

# position info
canal.instance.master.address=canal2:3306
canal.instance.master.journal.name=mysql-bin.000007
canal.instance.master.position=156
canal.instance.master.timestamp=
canal.instance.master.gtid=

# rds oss binlog
canal.instance.rds.accesskey=
canal.instance.rds.secretkey=
canal.instance.rds.instanceId=

# table meta tsdb info
canal.instance.tsdb.enable=true
#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb
#canal.instance.tsdb.dbUsername=canal
#canal.instance.tsdb.dbPassword=canal

canal.instance.standby.address = canal1:3306
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
#canal.instance.standby.gtid=

# username/password
canal.instance.dbUsername=canal
canal.instance.dbPassword=Canal_123
canal.instance.connectionCharset = UTF-8
# enable druid Decrypt database password
canal.instance.enableDruid=false
#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ==

# table regex
canal.instance.filter.regex=db1\\.tb\\d_\\d
# table black regex
canal.instance.filter.black.regex=mysql\\.slave_.*
# table field filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
#canal.instance.filter.field=test1.t_product:id/subject/keywords,test2.t_company:id/name/contact/ch
# table field black filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
#canal.instance.filter.black.field=test1.t_product:subject/product_image,test2.t_company:id/name/contact/ch

# mq config
#canal.mq.topic=example
# dynamic topic route by schema or table regex
canal.mq.dynamicTopic=db1:db1\\.tb\\d_\\d
#canal.mq.partition=0
# hash partition config
#canal.mq.partitionsNum=3
canal.mq.partitionHash=db1\\.tb\\d_\\d:idA
canal.mq.dynamicTopicPartitionNum=db\\d:3,db1:3
#################################################


# 需要开启心跳检查
canal.instance.detecting.enable = true 
# 心跳检查sql
canal.instance.detecting.sql = select 1
# 心跳检查频率
canal.instance.detecting.interval.time = 3 
# 心跳检查失败次数阀值,超过该阀值后会触发mysql链接切换,比如切换到standby机器上继续消费binlog
canal.instance.detecting.retry.threshold = 3  
# 心跳检查超过失败次数阀值后,是否开启master/standby的切换
canal.instance.detecting.heartbeatHaEnable = true 
[root@canal1 ~]# 

将example_kafka移到3台服务器的canal.deployer-1.1.5/conf下, canal会自动扫描到example_kafka

[root@canal1 ~]# pwd
/root
[root@canal1 ~]# 
[root@canal1 ~]# mv example_kafka canal.deployer-1.1.5/conf
[root@canal1 ~]#
[root@canal1 ~]# scp -r canal.deployer-1.1.5/conf/example_kafka root@canal2:/root/canal.deployer-1.1.5/conf/
instance.properties                                100% 2752     1.7MB/s   00:00    
[root@canal1 ~]# scp -r canal.deployer-1.1.5/conf/example_kafka root@canal3:/root/canal.deployer-1.1.5/conf/
instance.properties                                100% 2752     2.1MB/s   00:00    
[root@canal1 ~]# 

修改canal2和canal3的conf/example_kafka/instance.properties的canal.instance.mysql.slaveId参数

因为我们只将db1数据库的发送到了db1 topic, db2的则发送到了默认的example topic;而且db1 topic按idA进行了分区

2.3 kafka_sync.java代码的开发

import com.alibaba.otter.canal.protocol.Message;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.time.Duration;
import java.util.Arrays;
import java.util.Properties;

public class kafka_sync {

    public static void main(String[] args) {

        Properties props = new Properties();
        props.setProperty("bootstrap.servers", "192.168.23.31:9092,192.168.23.32:9092,192.168.23.33:9092");
        props.setProperty("group.id", "kafka_sync");
        props.setProperty("enable.auto.commit", "true");
        props.setProperty("auto.commit.interval.ms", "1000");
        // KafkaCanalConnector不能设置properties, auto.offset.reset设置为latest会重复消费kafka最后一条消息
        props.setProperty("auto.offset.reset", "earliest");

        props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        // 设置为Message类型
        props.setProperty("value.deserializer", "com.alibaba.otter.canal.client.kafka.MessageDeserializer");

        KafkaConsumer<String, Message> consumer = new KafkaConsumer<String, Message>(props);
        consumer.subscribe(Arrays.asList("db1"));
        try {
            while (true) {
                ConsumerRecords<String, Message> records = consumer.poll(Duration.ofMillis(100L));
                for (ConsumerRecord<String, Message> record : records) {

                    // 获取到了message, 后面的开发就可以参考<https://blog.csdn.net/yy8623977/article/details/118210041>进行开发了
                    long batchId = record.value().getId();
                    int batchSize = record.value().getEntries().size();

                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }


            }
        } finally {
            consumer.close();
        }


    }


}

测试的时候,可以更改group.id参数进行多次测试

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,下面是一个基于 CanalKafka 和 PySpark Streaming 的实时案例,用于将 MySQL 数据库中的增量数据读取并发送到 Kafka,并通过 PySpark Streaming 进行实时计算。具体步骤如下: 1. 首先需要安装并启动 CanalKafka。 2. 创建一个 Python 脚本,使用 Canal 客户端连接到 Canal Server,并订阅指定的 MySQL 数据库和表。然后将解析出的增量数据发送到 Kafka。 ```python from canal.client import Client from kafka import KafkaProducer import json # 创建Canal客户端对象 client = Client() client.connect(host='127.0.0.1', port=11111) client.check_valid(username=b'', password=b'') # 创建Kafka生产者对象 producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092') # 订阅指定的数据库和表 client.subscribe(client_id='1001', destination='example', filter='.*\\..*') try: while True: message = client.get(batch_size=100, timeout=100) for entry in message.get('entries'): if entry.entryType == 'ROWDATA': row_change = entry.rowChange event_type = row_change.eventType for row_data in row_change.rowDatas: row_dict = {} for column in row_data.afterColumns: row_dict[column.name] = column.value # 将解析出的增量数据发送到Kafka producer.send(topic='example', value=json.dumps(row_dict).encode('utf-8')) except Exception as e: print(e) finally: client.disconnect() ``` 上述代码中,首先创建了一个 Canal 客户端对象,并连接到 Canal Server。然后创建了一个 Kafka 生产者对象,用于将解析出的增量数据发送到 Kafka。接着,订阅了指定的 MySQL 数据库和表,并循环获取增量数据。对于每个增量数据,将其转换为字典格式,并使用 `json.dumps` 方法将字典编码为 JSON 字符串,并使用 Kafka 生产者将其发送到指定的 Kafka 主题中。 3. 创建一个 PySpark Streaming 应用程序,从 Kafka 主题中读取增量数据,并进行实时计算。例如,以下代码用于计算每隔5秒钟的单词计数: ```python from pyspark import SparkConf, SparkContext from pyspark.streaming import StreamingContext from pyspark.streaming.kafka import KafkaUtils import json # 创建SparkConf对象 conf = SparkConf().setAppName("Real-time Word Count") # 创建SparkContext对象 sc = SparkContext(conf=conf) # 创建StreamingContext对象,每隔5秒钟处理一次数据 ssc = StreamingContext(sc, 5) # 从Kafka读取数据 kafka_params = { "bootstrap.servers": "127.0.0.1:9092", "group.id": "example" } kafka_stream = KafkaUtils.createDirectStream( ssc, ["example"], kafka_params ) # 对Kafka中的每条消息进行解析并扁平化处理 messages = kafka_stream.map(lambda x: json.loads(x[1])) words = messages.flatMap(lambda x: x.values()) # 对每个单词进行计数 word_counts = words.map(lambda word: (word, 1)).reduceByKey(lambda a, b: a + b) # 打印计数结果 word_counts.pprint() # 启动StreamingContext并等待它完成 ssc.start() ssc.awaitTermination() ``` 上述代码中,首先创建了一个 `SparkConf` 对象,用于设置应用程序名称。然后创建了一个 `SparkContext` 对象,用于连接到 Spark 集群。接着,创建了一个 `StreamingContext` 对象,并设置每隔5秒钟处理一次数据。然后使用 `createDirectStream` 方法从 Kafka 主题中读取数据,并使用 `json.loads` 方法将 JSON 字符串转换为字典格式。接着,对字典中的值进行扁平化处理,并使用 `map` 和 `reduceByKey` 方法对每个单词进行计数。最后,使用 `pprint` 方法打印计数结果。最后启动 `StreamingContext` 并等待它完成。 运行上述代码后,您应该可以在终端看到类似以下的结果: ``` ------------------------------------------- Time: 2022-10-20 16:00:05 ------------------------------------------- (PySpark, 1) (Streaming, 1) (Hello, 1) (PySparkStreaming, 1) (World, 1) ------------------------------------------- Time: 2022-10-20 16:00:10 ------------------------------------------- (PySpark, 2) (Streaming, 1) (Hello, 2) (PySparkStreaming, 1) (World, 1) ``` 注意,以上代码仅提供了一个简单的示例,实际的 Canal 和 PySpark Streaming 应用程序可能需要更多的配置和代码来实现特定的需求。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值