Canal简介
Posted TGB-Earnest
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Canal简介相关的知识,希望对你有一定的参考价值。
一、Canal的搭建使用
Canal简介
我们项目中用的Canal的目的是,当数据库中数据有增删改查了,然后将这些数据做自动的处理。
Canal搭建
Canal分为Server和Client端。
它的原理和mysql的主从是很相似的,都是读取的bin log日志。
(1)Mysql服务器的设置
https://blog.csdn.net/yehongzhi1994/article/details/107880162
(2)安装Cannal
我下载的是1.1.5版本的canal.deployer
打开conf下面的properties文件
修改配置文件
#################################################
## mysql serverId , v1.0.26+ will autoGen
# canal.instance.mysql.slaveId=0
# enable gtid use true/false
canal.instance.gtidon=false
# position info
#canal.instance.master.address=127.0.0.1:3306
#数据库地址
canal.instance.master.address=192.168.241.6:3306
#binlog日志名称
canal.instance.master.journal.name=mysql-bin.000332
# mysql 主库连接时起始的binlog偏移量
canal.instance.master.position=49565061
#mysql主库连接时起始的binlog的时间戳
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 =
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
#canal.instance.standby.gtid=
# username/password 在MySQL服务器授权的账号密码
canal.instance.dbUsername=crm2021_dev
canal.instance.dbPassword=bfdds06fd
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=.*\\\\..*
# 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=mytest1.user,mytest2\\\\..*,.*\\\\..*
canal.mq.partition=0
# hash partition config
#canal.mq.partitionsNum=3
#canal.mq.partitionHash=test.table:id^name,.*\\\\..*
#canal.mq.dynamicTopicPartitionNum=test.*:4,mycanal:6
#################################################
然后启动bin中的startup.bat
(3) 客户端
引POM文件
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>1.1.5</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</exclusion>
</exclusions>
</dependency>
Client代码
package com.hc.zxd.cannal.deploy;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.util.List;
/**
* @Classname CannalClient
* @Description TODO
* @Date 2021/10/27 11:21
* @Author zhaoxiaodong
*/
@Component
public class CannalClient implements InitializingBean
private final static int BATCH_SIZE = 1000;
@Override
public void afterPropertiesSet() throws Exception
// 创建链接
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");
try
//打开连接
connector.connect();
//订阅数据库表,全部表
connector.subscribe(".*\\\\..*");
//回滚到未进行ack的地方,下次fetch的时候,可以从最后一个没有ack的地方开始拿
connector.rollback();
while (true)
//获取指定数量的数据
Message message = connector.getWithoutAck(BATCH_SIZE);
//获取批量ID
long batchId = message.getId();
//获取批量的数量
int size = message.getEntries().size();
//如果没有数据
if (batchId == -1 || size == 0)
try
//线程休眠2秒
Thread.sleep(2000);
catch (InterruptedException e)
e.printStackTrace();
else
//如果有数据,处理数据
printEntry(message.getEntries());
//进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。
connector.ack(batchId);
catch (Exception e)
e.printStackTrace();
finally
connector.disconnect();
/**
* @param entries
* @return void
* @Author zhaoxiaodong
* @Description 打印
* @Date 11:46 2021/10/27
*/
private static void printEntry(List<CanalEntry.Entry> entries)
for (CanalEntry.Entry entry : entries)
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND)
//开启/关闭事务的实体类型,跳过
continue;
// RowChange对象,包含了一行数据变化的所有特征
// 比如isDdl 是否是ddl变更操作 sql 具体的ddl sql beforeColumns afterColumns 变更前后的数据字段等等
CanalEntry.RowChange rowChange;
try
rowChange=CanalEntry.RowChange.parseFrom(entry.getStoreValue());
catch (Exception e)
throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
//获取操作类型: insert /update /delete类型
CanalEntry.EventType eventType = rowChange.getEventType();
//打印Header信息
System.out.println(String.format("================》; binlog[%s:%s] , name[%s,%s] , eventType : %s",
entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
eventType));
//判断是否是DDL语句
if (rowChange.getIsDdl())
System.out.println("================》;isDdl: true,sql:" + rowChange.getSql());
//获取RowChange对象里的每一行数据,打印出来
for (CanalEntry.RowData rowData : rowChange.getRowDatasList())
//如果是删除语句
if (eventType == CanalEntry.EventType.DELETE)
printColumn(rowData.getBeforeColumnsList());
//如果是新增语句
else if (eventType == CanalEntry.EventType.INSERT)
printColumn(rowData.getBeforeColumnsList());
//如果是更新的语句
else
//变更前的数据
System.out.println("---->;before");
printColumn(rowData.getBeforeColumnsList());
//变更后的数据
System.out.println("---->;after");
printColumn(rowData.getAfterColumnsList());
private static void printColumn(List<CanalEntry.Column> columns)
for (CanalEntry.Column column : columns)
System.out.println(column.getName()+":"+column.getValue());
测试
当我们修改数据库值的时候就可以看到变化了在控制台
以上是关于Canal简介的主要内容,如果未能解决你的问题,请参考以下文章