【Canal源码分析】client工作过程

本文详细分析了Canal集群客户端的工作过程,包括连接建立、订阅数据、获取与处理binlog、提交确认、回滚及断开连接等步骤。在连接阶段,涉及ClusterNodeAccessStrategy策略选择服务器,并通过zk节点获取server列表。订阅时,创建zk节点并发送订阅消息。获取数据使用getWithoutAck方法,处理binlog后手动ack。异常情况下,可能进行数据回滚或断开连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

client的工作过程,需要我们自己去编写对应的逻辑,我们目前只能从example写的例子来看。目前examle中提供了两个例子,一个是单机的,一个是集群的cluster,我们后续如果需要进行开发的话,其实也是开发我们自己的client,以及client的一些逻辑。我们主要看下集群的client是如何实现和消费的,又是怎么和server进行数据交互的。

我们来看看具体的代码:

protected void process() {
    int batchSize = 5 * 1024;
    while (running) {
        try {
            MDC.put("destination", destination);
            connector.connect();
            connector.subscribe();
            waiting = false;
            while (running) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    // try {
   
   
                    // Thread.sleep(1000);
                    // } catch (InterruptedException e) {
   
   
                    // }
                } else {
                    printSummary(message, batchId, size);
                    printEntry(message.getEntries());
                }

                connector.ack(batchId); // 提交确认
                // connector.rollback(batchId); // 处理失败, 回滚数据
            }
        } catch (Exception e) {
            logger.error("process error!", e);
        } finally {
            connector.disconnect();
            MDC.remove("destination");
        }
    }
}

这个的这样的过程是这样的
- 连接,connector.connect()
- 订阅,connector.subscribe
- 获取数据,connector.getWithoutAck()
- 业务处理
- 提交确认,connector.ack()
- 回滚,connector.rollback()
- 断开连接,connector.disconnect()

我们具体来看下。

一、建立连接

CanalConnector主要有两个实现,一个是SimpleCanalConnector,一个是ClusterCanalConnector,我们主要看下ClusterCanalConnector,这也是我们要用的一个模式。

我们用的时候,通过一个工厂类生成我们需要的Connector,这里的工厂类是CanalConnectors,里面包含了生成ClusterCanalConnector的方法。

public static CanalConnector newClusterConnector(String zkServers, String destination, String username,
                                                 String password) {
    ClusterCanalConnector canalConnector = new ClusterCanalConnector(username,
        password,
        destination,
        new ClusterNodeAccessStrategy(destination, ZkClientx.getZkClient(zkServers)));
    canalConnector.setSoTimeout(30 * 1000);
    return canalConnector;
}

用到的参数有zk的地址,canal的名称,数据库的账号密码。里面有个ClusterNodeAccessStrategy是用来选择client的策略,这个ClusterNodeAccessStrategy的构造方法里面有些东西需要我们关注下。

1.1 ClusterNodeAccessStrategy

public ClusterNodeAccessStrategy(String destination, ZkClientx zkClient){
    this.zkClient = zkClient;
    childListener = new IZkChildListener() {

        public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
            initClusters(currentChilds);
        }

    };

    dataListener = new IZkDataListener() {

        public void handleDataDeleted(String dataPath) throws Exception {
            runningAddress = null;
        }

        public void handleDataChange(String dataPath, Object data) throws Exception {
            initRunning(data);
        }

    };

    String clusterPath = ZookeeperPathUtils.getDestinationClusterRoot(destination);
    this.zkClient.subscribeChildChanges(clusterPath, childListener);
    initClusters(this.zkClient.getChildren(clusterPath));

    String runningPath = ZookeeperPathUtils.getDestinationServerRunning(destination);
    this.zkClient.subscribeDataChanges(runningPath, dataListener);
    initRunning(this.zkClient.readData(runningPath, true));
}

这边起了两个监听器,都是监听server端的活动服务器的。一个是获取所有的server列表,一个是获取活动的server服务器,都是从zk的对应节点上去取的。

1.2 连接connect

获取到CanalConnector之后,就是真正的连接了。在ClusterCanalConnector中,我们可以看到,其实他底层用的也是SimpleCanalConnector,只不过加了一个选择的策略。

public void connect() throws CanalClientException {
    if (connected) {
        return;
    }

    if (runningMonitor != null) {
        if (!runningMonitor.isStart()) {
            runningMonitor.start();
        }
    } else {
        waitClientRunning();
        if (!running) {
            return;
        }
        doConnect();
        if (filter != null) { // 如果存在条件,说明是自动切换,基于上一次的条件订阅一次
            subscribe(filter);
        }
        if (rollbackOnConnect) {
            rollback();
        }
    }

    connected = true;
}

如果是集群模式的客户端,那么这边的runningMonitor不为空,因为他进行了初始化。我们主要看下runningMonitor.start()里面的操作。

public void 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值