Canal使用

13 篇文章 0 订阅
12 篇文章 0 订阅

1. Canal 简介

Canal 是阿里巴巴开源的一款基于数据库增量日志解析,提供增量数据订阅和消费,支持 MySQL、Oracle、SqlServer 等多种数据库的数据订阅和消费的中间件,可以将数据库变更信息异步地推送到MQ中间件,如 Kafka、RocketMQ 等。

Canal 首先基于Google的开源协议protobuf定义一套数据格式,然后基于此协议解析各种数据库的增量日志,提供类似于MySQL replication的增量订阅&消费机制,支持丰富的sql语句过滤功能,同时提供多种数据输出格式,如json、protobuf等。Canal可以广泛应用于数据实时同步、数据分析、数据监控等场景。

2. Canal 安装

2.1 mysql开启binlog模式

1)查看当前 mysql 是否开启 binlog 模式。

SHOW VARIABLES LIKE '%log_bin%'

如果log_bin的值为OFF是未开启,为ON是已开启。

2)修改/etc/my.cnf 需要开启binlog模式。

[mysqld]
log-bin=mysql-bin
binlog-format=ROW
server_id=1

修改完成之后,重启mysqld的服务。

3)进入mysql

mysql -h localhost -u root -p

4)创建账号 用于测试使用

使用root账号创建用户并授予权限

create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;

2.2 Canal 下载安装

(这里我是在window下安装的)

windows下部署canal,服务端+客户端

1)下载地址:Releases · alibaba/canal · GitHub

2)解压压缩包。

3)配置文件:..\canal.deployer-1.1.5\conf\example.instance.properties

# position info
canal.instance.master.address=localhost:3306   #需要连接的数据库地址及端口
...
# username/password
canal.instance.dbUsername=canal # 数据库账号
canal.instance.dbPassword=canal # 数据库密码
...
canal.instance.defaultDatabaseName = 数据库名   #默认一个数据库

4)开启服务

3. 项目监控数据变化

3.1 导入 maven

<dependency>
	<groupId>com.xpand</groupId>
	<artifactId>starter-canal</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

导入 starter-canal 依赖导不上:畅购商城canal依赖,搭建微服务的问题_xiaopeng_thriller的博客-CSDN博客

3.2 配置文件

canal:
    client:
        instances:
            example:
                host: localhost
                port: 11111
                batchSize: 9099

3.3 启动类

@SpringBootApplication
@EnableCanalClient
public class CanalApplication {

    public static void main(String[] args) {
        SpringApplication.run(CanalApplication.class, args);
    }
}

3.4 监听类

import com.alibaba.otter.canal.protocol.CanalEntry;
import com.xpand.starter.canal.annotation.CanalEventListener;
import com.xpand.starter.canal.annotation.ListenPoint;

@CanalEventListener
public class BusinessListener {

    @ListenPoint(schema = "数据库名", table = {"表名"})
    public void adUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
        System.err.println("数据发生变化");
        rowData.getBeforeColumnsList().forEach((c) -> System.err.println("更改前数据: " + c.getName() + " :: " + c.getValue()));

        rowData.getAfterColumnsList().forEach((c) -> System.err.println("更改后数据: " + c.getName() + " :: " + c.getValue()));
    }
}

修改表的数据之后:

Canal 数据监控的使用_@enablecanalclient_YKenan的博客-CSDN博客


第二种方式

package com.cigna.hmc.activity.canal;

import java.net.InetSocketAddress;
import java.util.List;

import com.cigna.hmc.activity.common.util.DateUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
import com.alibaba.otter.canal.protocol.Message;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class CannalClient implements InitializingBean {
    @Value("${canal.monitor.table}")
    private String monitorTable;

    private final static int BATCH_SIZE = 1000;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 创建链接
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("10.141.162.89", 11111), "example", "canal", "canal");
        try {
            // 打开连接
            connector.connect();
            // 订阅数据库表,全部表
//            connector.subscribe(".*..*");
            connector.subscribe(monitorTable);
            // 回滚到未进行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 {
                        // 线程休眠1秒
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                else {
                    // 如果有数据,处理数据
                    printEntry(message.getEntries());
                }
                // 进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。
                connector.ack(batchId);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            connector.disconnect();
        }
    }

    /**
     * 打印canal server解析binlog获得的实体类信息
     */
    private static void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                // 开启/关闭事务的实体类型,跳过
                continue;
            }
            // RowChange对象,包含了一行数据变化的所有特征
            // 比如isDdl 是否是ddl变更操作 sql 具体的ddl sql beforeColumns afterColumns 变更前后的数据字段等等
            RowChange rowChage;
            try {
                rowChage = 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类型
            EventType eventType = rowChage.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 (rowChage.getIsDdl()) {
                System.out.println("================》;isDdl: true,sql:" + rowChage.getSql());
            }
            // 获取RowChange对象里的每一行数据,打印出来
            for (RowData rowData : rowChage.getRowDatasList()) {
                // 如果是删除语句
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                    // 如果是新增语句
                }
                else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                    // 如果是更新的语句
                }
                else {
                    // 变更前的数据
                    System.out.println("------->; before");
                    printColumn(rowData.getBeforeColumnsList());
                    // 变更后的数据
                    System.out.println("------->; after");
                    printColumn(rowData.getAfterColumnsList());
                }
                System.out.println("time:" + DateUtil.getNow());
            }
        }
    }

    private static void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }

}
		<dependency>
			<groupId>top.javatool</groupId>
			<artifactId>canal-spring-boot-starter</artifactId>
			<version>1.2.1-RELEASE</version>
		</dependency>


参考文档:

数据同步解决方案-canal与rabbitmq_canal集成rabbitmq没生效_ICoder_Next的博客-CSDN博客

Canal配置文件详解_zhou12314456的博客-CSDN博客

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Canal是阿里巴巴开源的一款基于MySQL的数据增量订阅&消费框架。使用Canal可以将MySQL数据库中的数据变更事件实时同步到其他数据存储或者消息系统中。以下是在Java项目中使用Canal的简单步骤: 1. 引入Canal客户端依赖 在Java项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba.otter</groupId> <artifactId>canal.client</artifactId> <version>1.1.4</version> </dependency> ``` 2. 配置Canal客户端 在Java项目中创建Canal客户端并配置连接参数,可以参考以下示例代码: ```java import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnectors; public class CanalClient { public static void main(String[] args) { // 创建Canal连接器 CanalConnector connector = CanalConnectors.newSingleConnector( new InetSocketAddress("127.0.0.1", 11111), "example", "", ""); // 连接到Canal服务端 connector.connect(); connector.subscribe(".*\\..*"); connector.rollback(); while (true) { // 获取数据变更事件 Message message = connector.getWithoutAck(100); long batchId = message.getId(); int size = message.getEntries().size(); if (batchId == -1 || size == 0) { continue; } // 处理数据变更事件 for (CanalEntry.Entry entry : message.getEntries()) { if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) { RowChange rowChange; try { rowChange = RowChange.parseFrom(entry.getStoreValue()); } catch (Exception e) { throw new RuntimeException("ERROR ## parser of eromanga-event has an error", e); } EventType eventType = rowChange.getEventType(); String tableName = entry.getHeader().getTableName(); for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) { if (eventType == EventType.DELETE) { // TODO: 处理删除事件 } else if (eventType == EventType.INSERT) { // TODO: 处理插入事件 } else if (eventType == EventType.UPDATE) { // TODO: 处理更新事件 } } } } // 提交确认 connector.ack(batchId); } } } ``` 3. 启动Canal客户端 在Java项目中启动Canal客户端,即可实现对MySQL数据库的数据变更事件的实时订阅和消费: ```java CanalClient client = new CanalClient(); client.run(); ``` 以上是在Java项目中使用Canal的简单步骤,具体实现方式可以根据实际情况进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值