• canal笔记


    工作原理

    canal的工作原理就是把自己伪装成MySQL slave,模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,MySQL mater收到canal发送过来的dump请求,开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地,比如MySQL,Kafka,Elastic Search等等。

    一、mysql创建一个可用的账号

    -- 使用命令登录:mysql -u root -p
    -- 创建用户 用户名:canal 密码:Canal@123456
    create user 'canal'@'%' identified by 'Canal@123456';
    -- 授权 *.*表示所有库
    grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'canal'@'%' identified by 'Canal@123456';

    数据库开启binlog

    查看binglog日志及开启方法请移步: https://www.cnblogs.com/kaibindirver/p/16540069.html

    一、下载canal.deployer 及启动

    https://github.com/alibaba/canal/releases

    官方说明: https://github.com/alibaba/canal/wiki/QuickStart

    解压后对其配置文件进行修改

    配置文件conf/example/instance.propertiesv

    ## mysql serverId--随便写但是不要重复
    canal.instance.mysql.slaveId = 1234
    #position info,需要改成自己的数据库信息
    canal.instance.master.address = 192.168.1.103:3317
    canal.instance.master.journal.name = mysql-bin.000005
    canal.instance.master.position = 
    canal.instance.master.timestamp = 
    #canal.instance.standby.address = 
    #canal.instance.standby.journal.name =
    #canal.instance.standby.position = 
    #canal.instance.standby.timestamp = 
    #username/password,需要改成自己的数据库信息
    canal.instance.dbUsername = canal  
    canal.instance.dbPassword = Canal@123456
    canal.instance.defaultDatabaseName =
    canal.instance.connectionCharset = UTF-8
    #table regex # mysql 数据解析表的-只接收某表,多个表用,隔开 见 https://blog.csdn.net/leilei1366615/article/details/108819651
    canal.instance.filter.regex = .\*\\\\..\*
    //mysql 数据解析关注的表,Perl正则表达式.多个正则之间以逗号(,)分隔,转义符需要双斜杠(\) 常见例子:1. 所有表:.* or .\… 2. canal schema下所有表: canal\…* 3. canal下的以canal打头的表:canal\.canal.* 4. canal schema下的一张表:canal\.test15. 多个规则组合使用:canal\…*,mysql.test1,mysql.test2 (逗号分隔)

    这里开始的时候制定了binlog文件,后续数据库binglog变了,canal.deployer也会自动记录在上面instance.properties配置文件下的meta.dat里面记录最新的

    canal.instance.filter.regex  指定接收某库某表的,adapter只会接收到指定的库表信息 对呀log日志文件就不会你那么大

    canal.instance.filter.regex = eee_mlz_bbb\…*  只接收eee_mlz_bbbeee_mlz_bbb库下的所有表信息变更

    在bin目录下启动

    sh startup.sh   

     

    /canal/canal.deployer-1.1.5/logs/canal/canal.log 

    log会打印出服务的ip和暴露的端口

    /logs/example/example.log

    log这个是binlog的变动

     

    遇到问题解决:

    canal deployer运行出现 org.h2.jdbc.JdbcSQLException: Wrong user name or password

    解决: 删除canal.deployer/conf/example中的XX.mv.db文件

    参考: https://blog.csdn.net/weixin_41098099/article/details/115656887

    目录: /conf/example/meta.dat 

    有时候客户端接收不到数据,是因为meta.dat里面指定的log文件不对,把他删了重新启动即可

    二、编写客户端,从canal获取变更的消息

    1、创建springboot项目

    2、首先引入maven依赖:

    <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.client</artifactId>
        <version>1.1.4</version>
    </dependency>

    目录结构

    在CannalClient类使用Spring Bean的生命周期函数afterPropertiesSet():

    @Component
    public class CannalClient implements InitializingBean {
    
        private final static int BATCH_SIZE = 1000;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // 创建链接   ,这里输入的是cannel的服务ip和他默认暴露的端口11111
            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();
            }
        }
    
        /**
         * 打印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());
                    }
                }
            }
        }
    
        private static void printColumn(List<Column> columns) {
            for (Column column : columns) {
                System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
            }
        }
    }

    当数据库发生变化时,就会收到消息,如果收不到查看下 cannel服务的/logs目录下各log文件,看有没有报错的信息

    这里有个别人写好可 以直接插入数据库的到时看看  https://blog.csdn.net/qq_36971119/article/details/122856561

    三、同步到其他数据库的方法

    1、下载adapter文件

    https://github.com/alibaba/canal/releases

    官方说明 https://github.com/alibaba/canal/wiki/ClientAdapter

    (注意这个文件要放到没有中文的目录下面!!!!!!!!---晕死踩坑2次) 

    有中文会启动报  Caused by: java.lang.RuntimeException: Config dir not found.

    目录文件

    2、配置配置文件及启动

    启动器文件  application.yml

    server:
      port: 8081
    spring:
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
        default-property-inclusion: non_null
    
    canal.conf:
      mode: tcp #tcp kafka rocketMQ rabbitMQ
      flatMessage: true
      zookeeperHosts:
      syncBatchSize: 1000
      retries: 0
      timeout:
      accessKey:
      secretKey:
      consumerProperties:
        # canal tcp consumer
        canal.tcp.server.host: 127.0.0.1:11111
        canal.tcp.zookeeper.hosts:
        canal.tcp.batch.size: 500
        canal.tcp.username:
        canal.tcp.password:
        # kafka consumer
        kafka.bootstrap.servers: 127.0.0.1:9092
        kafka.enable.auto.commit: false
        kafka.auto.commit.interval.ms: 1000
        kafka.auto.offset.reset: latest
        kafka.request.timeout.ms: 40000
        kafka.session.timeout.ms: 30000
        kafka.isolation.level: read_committed
        kafka.max.poll.records: 1000
        # rocketMQ consumer
        rocketmq.namespace:
        rocketmq.namesrv.addr: 127.0.0.1:9876
        rocketmq.batch.size: 1000
        rocketmq.enable.message.trace: false
        rocketmq.customized.trace.topic:
        rocketmq.access.channel:
        rocketmq.subscribe.filter:
        # rabbitMQ consumer
        rabbitmq.host:
        rabbitmq.virtual.host:
        rabbitmq.username:
        rabbitmq.password:
        rabbitmq.resource.ownerId:
    
    #  srcDataSources:
    #    defaultDS:
    #      url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
    #      username: root
    #      password: 121212
      canalAdapters:
      - instance: example # canal instance Name or mq topic name  --给要同步的目标数据库启个别名
        groups:
        - groupId: g1
          outerAdapters:
          - name: logger    //这个注释掉貌似少了些信息,canal.deployer 配置获取的表命中才打出日志
          - name: rdb
            key: mysql1
            properties:
              jdbc.driverClassName: com.mysql.jdbc.Driver
              jdbc.url: jdbc:mysql://192.168.1.103:3306/max?useUnicode=true
              jdbc.username: root
              jdbc.password: 123456
         # - name: rdb
         #   key: oracle1
         #   properties:
         #     jdbc.driverClassName: oracle.jdbc.OracleDriver
         #     jdbc.url: jdbc:oracle:thin:@localhost:49161:XE
         #     jdbc.username: mytest
         #     jdbc.password: m121212
    #      - name: rdb
    #        key: postgres1
    #        properties:
    #          jdbc.driverClassName: org.postgresql.Driver
    #          jdbc.url: jdbc:postgresql://localhost:5432/postgres
    #          jdbc.username: postgres
    #          jdbc.password: 121212
    #          threads: 1
    #          commitSize: 3000
    #      - name: hbase
    #        properties:
    #          hbase.zookeeper.quorum: 127.0.0.1
    #          hbase.zookeeper.property.clientPort: 2181
    #          zookeeper.znode.parent: /hbase
    #      - name: es
    #        hosts: 127.0.0.1:9300 # 127.0.0.1:9200 for rest mode
    #        properties:
    #          mode: transport # or rest
    #          # security.auth: test:123456 #  only used for rest mode
    #          cluster.name: elasticsearch
    #        - name: kudu
    #          key: kudu
    #          properties:
    #            kudu.master.address: 127.0.0.1 # ',' split multi address

    适配器文件

    同步max库 student2 表

    dataSourceKey: defaultDS        # 源数据源的key, 对应上面配置的srcDataSources中的值
    destination: example            # cannal的instance或者MQ的topic
    groupId: g1
    outerAdapterKey: mysql1       # adapter key, 对应上面配置outAdapters中的key
    concurrent: true                # 是否按主键hase并行同步, 并行同步的表必须保证主键不会更改及主键不能为其他同步表的外键!!
    dbMapping:
      database: 123            # 源数据源的database/shcema
      table: student                  # 源数据源表名
      targetTable: student2   # 目标数据源的库名.表名--我在启动器指定了数据库的库了
      targetPk:                     # 主键映射
        id: id                    # 如果是复合主键可以换行映射多个
     # mapAll: true                 # 是否整表映射, 要求源表和目标表字段名一模一样 (如果targetColumns也配置了映射,则以targetColumns配置为准)
      targetColumns:                # 字段映射, 格式: 目标表字段: 源表字段, 如果字段名一样源表字段名可不填
        id: id
        name2: name
    
    # dbMapping:
    #  mirrorDb: true
    #  database: 123

    同步max库 tb_commodity_info 表

    dataSourceKey: defaultDS        # 源数据源的key, 对应上面配置的srcDataSources中的值
    destination: example            # cannal的instance或者MQ的topic
    groupId: g1
    outerAdapterKey: mysql1       # adapter key, 对应上面配置outAdapters中的key
    concurrent: true                # 是否按主键hase并行同步, 并行同步的表必须保证主键不会更改及主键不能为其他同步表的外键!!
    dbMapping:
      database: 123            # 源数据源的database/shcema
      table: tb_commodity_info                  # 源数据源表名
      targetTable: tb_commodity_info   # 目标数据源的库名.表名 ----我在启动器指定了数据库的库了
      targetPk:                     # 主键映射
        id: id                    # 如果是复合主键可以换行映射多个
      mapAll: true                 # 是否整表映射, 要求源表和目标表字段名一模一样 (如果targetColumns也配置了映射,则以targetColumns配置为准)
      # targetColumns:                # 字段映射, 格式: 目标表字段: 源表字段, 如果字段名一样源表字段名可不填
      #   id: id
      #   name2: name
    
    # dbMapping:
    #  mirrorDb: true
    #  database: 123

    在bin目录下启动

    sh startup.sh   

    3、操作控制接口

    查询所有订阅同步的canal destination 的状态  --(这个可以查询后说明服务已启动好了,不然还得等等)

    http://127.0.0.1:8081/destinations

    查看单独某个目标数据库

    http://127.0.0.1:8081/syncSwitch/example

    关闭别名为example的目标数据库同步

    curl http://127.0.0.1:8081/syncSwitch/example/off -X PUT

    开启别名为example的目标数据库同步

    curl http://127.0.0.1:8081/syncSwitch/example/on -X PUT     

    有个查看目标数据库同步数据的 请求 回头研究下

    参考文档:

    简单实现cannel入门---canal.deployer

    https://blog.csdn.net/yehongzhi1994/article/details/107880162

    cannel往mq或redis发

    https://blog.csdn.net/yehongzhi1994/article/details/108034330

    adapter配置参考

    https://blog.csdn.net/weixin_39748445/article/details/113231208

    https://blog.csdn.net/qq_29860591/article/details/110217909 --主要参考了这个

    https://www.wenjiangs.com/doc/client-adapter  ---貌似出处在这里

    视频

    https://www.bilibili.com/video/BV1jY41177Qh?spm_id_from=333.337.search-card.all.click&vd_source=caabcbd2a759a67e2a3de8acbaaf08ea

    官网wiki介绍:

    https://github.com/alibaba/canal/wiki

    这篇挺全的还有个canal的后台可以操作

    https://blog.csdn.net/leilei1366615/article/details/108819651

    源码去启动见这篇

    https://blog.csdn.net/wx2207/article/details/121538475

    部署到服务器

    sh命令后面要加&,不然无法使用2个sh的命令,不加会报下面的错误

    sh startup.sh &

    let: 命令无法识别解决: 

     sudo dpkg-reconfigure dash   选择 "否", 表示用bash代替dash

    https://www.cnblogs.com/fanjp666888/p/10022038.html

  • 相关阅读:
    《将博客搬至CSDN》
    Ubuntu 安装 maven
    Ubuntu jdk1.8安装
    spring整合jms
    jms入门
    MySQL 3306端口开启
    黑窗口下mysql导出导入数据库
    PHP 爬虫体验(三)
    解决nvm安装的node使用sudo npm报错的问题
    PHP 爬虫体验(二)
  • 原文地址:https://www.cnblogs.com/kaibindirver/p/16618420.html
Copyright © 2020-2023  润新知