• canal


    canal 是阿里巴巴开源的 MySQL binlog 增量订阅&消费组件

    canal 工作原理:

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

    本文使用的 canal 版本为 v1.1.4

    一、canal 安装与配置

    安装与配置可以参考:QuickStart,我的 conf/example/instance.properties 配置:

    ## mysql serverId , v1.0.26+ will autoGen
    canal.instance.mysql.slaveId=1234
    
    ... ...
    
    canal.instance.master.address=192.168.137.1:3306
    
    ... ...
    
    canal.instance.dbUsername=canal
    canal.instance.dbPassword=canal
    canal.instance.connectionCharset = UTF-8
    
    ... ...
    

    二、客户端使用

    添加引用:

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

    代码:

    @Component
    public class CanalRunner implements CommandLineRunner {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(CanalRunner.class);
    
        @Override
        public void run(String... args) throws Exception {
    
            CanalConnector connector = CanalConnectors.newSingleConnector(
                    new InetSocketAddress("192.168.137.101", 11111)
                    , "example"
                    , "canal", "canal");
            connector.connect();
            connector.subscribe(".*\..*");
            connector.rollback();
    
            while (true) {
                Message message = connector.getWithoutAck(100); // 获取指定数量的数据
                long batchId = message.getId();
                if (batchId == -1 || message.getEntries().isEmpty()) {
                    Thread.sleep(1000);
                    continue;
                }
    
                try {
                    printEntries(message.getEntries());
                    connector.ack(batchId);// 提交确认,消费成功,通知server删除数据
                } catch (Exception e) {
    //                connector.rollback(batchId);// 处理失败, 回滚数据,后续重新获取数据
                }
    
            }
    
        }
    
        private void printEntries(List<CanalEntry.Entry> entries) throws Exception {
            for (CanalEntry.Entry entry : entries) {
                if (entry.getEntryType() != CanalEntry.EntryType.ROWDATA) {
                    continue;
                }
    
                CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
    
                CanalEntry.EventType eventType = rowChange.getEventType();
    
                LOGGER.info("数据库名: {}, 数据表名: {}, 事件: {}"
                        , entry.getHeader().getSchemaName(), entry.getHeader().getTableName(), eventType);
    
                for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                    switch (rowChange.getEventType()) {
                        case INSERT:
                            printColumns(rowData.getAfterColumnsList());
                            break;
                        case UPDATE:
                            LOGGER.info("更新前数据:");
                            printColumns(rowData.getBeforeColumnsList());
                            LOGGER.info("更新后数据:");
                            printColumns(rowData.getAfterColumnsList());
                            break;
                        case DELETE:
                            printColumns(rowData.getBeforeColumnsList());
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    
        private void printColumns(List<CanalEntry.Column> columns) {
            Map<String, Object> map = new HashMap<>();
            for(CanalEntry.Column column : columns) {
                map.put(column.getName(), column.getValue());
            }
    
            ObjectMapper objectMapper = new ObjectMapper();
    
            try {
                LOGGER.info(objectMapper.writeValueAsString(map));
            } catch (JsonProcessingException e) {
            }
        }
    }
    

    CanalConnector 中的参数来自于 /conf/canal.properties:

      
    port canal.port
    destination canal.destinations
    username canal.instance.tsdb.dbUsername
    password canal.instance.tsdb.dbPassword

    参考

    1. canal
    2. 史上最全的分布式数据同步中间间canal 之入门篇
  • 相关阅读:
    面向过程, 面向对象, 类和对象, 类与数据类型
    python函数
    简单的登录注册函数
    Java遍历包中所有类
    spring boot jar启动
    过期算法
    负载均衡算法
    spring boot druid mybatis多数据源
    多线程wait和notify实现1212
    多线程售票
  • 原文地址:https://www.cnblogs.com/victorbu/p/13064256.html
Copyright © 2020-2023  润新知