Canal(基于mysql数据库binlog的增量订阅及消费)的简单使用

一、背景

近期想试试canal和maxwell两个工具去同步数据库,先整canal。canal主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。

二、canal的工作原理

Canal工作原理

  1. canal模拟MySQL slave的交互协议,伪装自己为MySQL slave,向MySQL master发送dump协议
  2. mysql master收到dump请求,开始推送binary log为slave
  3. canal解析binary log对象(原始为byte流)

三、安装Canal

1.MySQL配置

.检查binlog

检查是否打开binlog,Value为ON即为打开

mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+
1 row in set (0.01 sec)

如果为OFF,则需要开启binlog
如何开启mysql的binlog日志呢?
在my.cnf主配置文件中直接添加三行

log_bin=ON
log_bin_basename=/var/lib/mysql/mysql-bin
log_bin_index=/var/lib/mysql/mysql-bin.index

.检查binlog_format

检查binlog_format的值是否为ROW

mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

如果没开启
则修改 my.cnf (Mac 修改 /usr/local/mysql/my.cnf 无效,需要修改 /etc/my.cnf)
在mysqld下面添加

binlog_format=ROW
log-bin=mysql-bin

然后重启 mysqld

.创建Canal工具访问MySQL账号

CREATE USER canal IDENTIFIED BY 'canal';  
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;

2.Canal配置

.下载Cancl

# 1.下载包
wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz
# 2.解压包至 /root/canal
tar -zxvf canal.deployer-1.1.5.tar.gz -C /root/canal

.配置一个instance

instance:一个instance就是一个消息队列,一个canal server可以存在多个instance,每个instance都是独立的可以拉取不同或相同数据库的消息

# 1.下载包
wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz
# 2.解压包至 /root/canal
tar -zxvf canal.deployer-1.1.5.tar.gz -C /root/canal
.修改instance的配置
 vi /root/canal/conf/example/instance.properties
# mysql集群配置中的serverId概念,需要保证和当前mysql集群中id唯一
# canal.instance.mysql.slaveId=222
# mysql主库链接地址
canal.instance.master.address=127.0.0.1:3306(数据库所在服务器的ip地址)
# mysql主库链接时起始的binlog文件
canal.instance.master.journal.name=
# mysql主库链接时起始的binlog偏移量
canal.instance.master.position=
# mysql主库链接时起始的binlog的时间戳
canal.instance.master.timestamp=

# mysql数据库帐号
canal.instance.dbUsername=canal
# mysql数据库密码
canal.instance.dbPassword=canal
# mysql 数据解析编码
canal.instance.connectionCharset = UTF-8

# mysql 数据解析关注的表,Perl正则表达式,即我们需要关注那些库和那些表的binlog数据,也可以在canal client api中手动覆盖
canal.instance.filter.regex=.*\\..*
# table black regex
# mysql 数据解析表的黑名单,表达式规则见白名单的规则
canal.instance.filter.black.regex=mysql\\.slave_.*

canal.instance.filter.regex配置:

  • 所有表:.* or .\…
  • canal schema下所有表: canal\…*
  • canal下的以canal打头的表:canal\.canal.*
  • canal schema下的一张表:canal\.test1
  • 多个规则组合使用:canal\…*,mysql.test1,mysql.test2 (逗号分隔)

.canal.properties配置

 vi conf/canal.properties
# canal server绑定的本地IP信息,如果不配置,默认选择一个本机IP进行启动服务
canal.ip = 127.0.0.1(或所在服务器IP地址,我用127.0.0.1获取不到消息,用服务器IP成功)
# canal server提供socket服务的端口
canal.port = 11111
# canal 服务的用户名(客户端连接的时候需要这个用户名和密码,也可以不配置)
canal.user = canal
# canal 服务的密码
canal.passwd = 123456
# 当前server上部署的instance列表,此处写 example ,即conf目录下必须要有一个example文件夹,如果有多个instance,则以英文的逗号隔开
canal.destinations = example

3.启动Canal

sh /root/canal/bin/startup.sh

四. 客户端消费数据

1.在项目中引入Canal依赖

<dependencies>
  <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>
  </dependency>
  <dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.common</artifactId>
    <version>1.1.5</version>
  </dependency>
</dependencies>

2.代码

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 java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class CanalClientSample {
    public static void main(String[] args) {
        String destination = "customer";
        // 创建一个 canal 链接
        CanalConnector canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), destination, "admin", "123456");
        // 链接对应的canal server
        canalConnector.connect();
        // 订阅那个库的那个表等
        /**
         * 订阅规则
         * 1.  所有表:.*   or  .*\\..*
         * 2.  canal schema下所有表: canal\\..*
         * 3.  canal下的以canal打头的表:canal\\.canal.*
         * 4.  canal schema下的一张表:canal\\.test1
         * 5.  多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
         */
        canalConnector.subscribe("canal\\..*");
        // 回滚到未进行 #ack 的地方,下次fetch的时候,可以从最后一个没有 #ack 的地方开始拿
        canalConnector.rollback();
        int batchSize = 1000;
        while (true) {
            // 获取一批数据,不一定会获取到 batchSize 条
            Message message = canalConnector.getWithoutAck(batchSize);
            // 获取批次id
            long batchId = message.getId();
            // 获取数据
            List<CanalEntry.Entry> entries = message.getEntries();
            if (batchId == -1 || entries.isEmpty()) {
                System.out.println("没有获取到数据");
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            for (CanalEntry.Entry entry : entries) {
                if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
                    continue;
                }

                CanalEntry.RowChange rowChange;
                try {
                    rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                } catch (Exception e) {
                    throw new RuntimeException("解析binlog数据出现异常 , data:" + entry.toString(), e);
                }

                CanalEntry.EventType eventType = rowChange.getEventType();
                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));

                if (eventType == CanalEntry.EventType.QUERY || rowChange.getIsDdl()) {
                    System.out.println("sql => " + rowChange.getSql());
                }

                for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                    if (eventType == CanalEntry.EventType.DELETE) {
                        printColumn(rowData.getBeforeColumnsList());
                    } else if (eventType == CanalEntry.EventType.INSERT) {
                        printColumn(rowData.getAfterColumnsList());
                    } else {
                        System.out.println("-------> before");
                        printColumn(rowData.getBeforeColumnsList());
                        System.out.println("-------> after");
                        printColumn(rowData.getAfterColumnsList());
                    }
                }
            }
            canalConnector.ack(batchId);
        }
    }

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

3.测试结果

在这里图片描述

4.关闭Canal

sh /root/canal/bin/stop.sh
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值