阿里开源插件Canal集群搭建过程

目录

 

一、下载文件

二、解压文件

三、配置两个canal-server

四、canal-client

五、canal客户端集群


一、下载文件

开源插件地址canal.deployer-1.1.3.tar.gz

二、解压文件

对应的服务器上,目录如下

三、配置两个canal-server

需要先配置zookeeper,这个自行百度

canal-server所有的配置都在conf中; 

第一台相关配置相关文件

1、canal.properties,如下标红项,需要正确配置,其余配置使用默认
canal.id = 1 #canal服务id,目前没有实际意义
canal.ip = 
canal.port = 11111 #canal服务socket监听端口,代码中连接canal-server时,使用此段口连接
canal.metrics.pull.port = 11112
canal.zkServers =192.168.228.128:2181 #zookeeper服务地址端口

canal.destinations = example   #表示实例的配置文件instance.properties地址

2、example/instance.properties配置

#################################################
## mysql serverId , v1.0.26+ will autoGen
canal.instance.mysql.slaveId=1234 #每个canal-server都是master数据库的slave,此处为slave的唯一标识

# position info
canal.instance.master.address=192.168.5.65:3306 #master的地址
canal.instance.master.journal.name=  #mysql主库链接时起始的binlog文件,默认:无
canal.instance.master.position=  #mysql主库链接时起始的binlog偏移量,默认:无
canal.instance.master.timestamp= #mysql主库链接时起始的binlog的时间戳,默认:无
canal.instance.master.gtid=

# table meta tsdb info
canal.instance.tsdb.enable=true

# username/password
canal.instance.dbUsername=canal   #mysql数据库帐号
canal.instance.dbPassword=canal   #mysql数据库密码
canal.instance.connectionCharset = UTF-8 #mysql数据库编码
# enable druid Decrypt database password
canal.instance.enableDruid=false
#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ==
canal.instance.filter.regex=.*\\..*
canal.instance.filter.black.regex=
canal.mq.topic=example
canal.mq.partition=0

第二台机器配置需默认修改的地方

1、canal.properties,如下标红项,需要正确配置,其余配置使用默认

canal.id = 2

canal.port = 11112 #canal服务socket监听端口,代码中连接canal-server时,使用此段口连接

canal.metrics.pull.port = 12112

2、example/instance.properties配置

canal.instance.mysql.slaveId=1235

 

四、canal-client

package canal;

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;


public class ClientSample {
    public static void main(String args[]) {
        // 单连接
//        CanalConnector connector = CanalConnectors.newSingleConnector(
//                new InetSocketAddress("192.168.228.128", 11111), "example", "", "");
        // 集群连接
        //CanalConnector connector = CanalConnectors.newClusterConnector("192.168.113.101:2181,192.168.113.102:2181,192.168.113.103:2181/canal/cluster1", "canal_test", "", "");
        CanalConnector connector = CanalConnectors.newClusterConnector("192.168.228.128:2181", "example", "", "");
        //计数器
        int emptyCount = 0;
        //一次最多拉多少条Message
        //注意:

        int batchSize = 1000;
        try {
            //和server建立连接
            connector.connect();
            //订阅表
            connector.subscribe("test.user");
            //回滚到上次ack的位置
            connector.rollback();
            //最大空闲次数
            int maxEmptyCount = 1000;
            //死循环去拉取数据
            while (emptyCount < maxEmptyCount) {
                //尝试最多拿batchSize条记录
                //注意:getWithoutAck一次,对应一个Message。
                //一个Message有一个MessageID,还有一个List<CanalEntry.Entry>
                Message message = connector.getWithoutAck(batchSize);
                long batchId = message.getId();
                int size = message.getEntries().size();
                //没有数据---等待
                if (batchId == -1 || size == 0) {
                    emptyCount++;
//                    System.out.println("empty count : " + emptyCount);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    //拿到数据---开始解析--发送到目的地如kafka/elasticsearch/redis/hbase...等
                    emptyCount = 0;
                    //这里打印内容
                    printEntry(message.getEntries());
                }

                connector.ack(batchId); // 提交确认
                //connector.rollback(batchId); // 处理失败, 回滚数据
            }

            System.out.println("empty too many times, exit");
        } finally {
            connector.disconnect();
        }
    }

    private static void printEntry(List<CanalEntry.Entry> entrys) {
        for (CanalEntry.Entry entry : entrys) {
            //该条数据的数据类型是事务开始或事务结束,不是binlog 二进制数据本身,就跳过继续处理
            //注意:这里是有bug的,当mysql开启binlog,且为Row行模式,且开启了在binlog中显示原始SQL。
            //这时就会有一种新增的类型:CanalEntry.EventType.QUERY
            // 原始
            //if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
            //    continue;
            //}
            // 修改为 只保留binlog部分对应的Entry并解析
            if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN //跳过事务开始的Entry
                    || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND //跳过事务结束的Entry
                    || entry.getHeader().getEventType() == CanalEntry.EventType.QUERY //跳过事务为原始SQL的Entry
                    ) {
                continue;
            }
            //得到当前行变化的数据Before、After
            CanalEntry.RowChange rowChage = null;
            try {
                rowChage = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
            } catch (Exception e) {
                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                        e);
            }

            //事件类型
            CanalEntry.EventType eventType = rowChage.getEventType();
            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(), //binlog文件名
                    entry.getHeader().getLogfileOffset(),//binlog offset
                    entry.getHeader().getSchemaName(),//库名
                    entry.getHeader().getTableName(),//表名
                    eventType //事件类型
            ));

            for (CanalEntry.RowData rowData : rowChage.getRowDatasList()) {
                //INSERT
                if (eventType == CanalEntry.EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                //DELETE
                } else if (eventType == CanalEntry.EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                //UPDATE
                } else if (eventType == CanalEntry.EventType.UPDATE) {
                    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() + "    update=" + column.getUpdated());
        }
    }

}

五、canal客户端集群

将canal-client 相同的消费代码部署多分,zk就能保证同一时刻,仅仅一份代码子运行,当其中一个down掉,另一个会在固定时间内被激活;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值