Canal学习笔记

 

canal部署安装

上面我们知道canal是通过把自己伪装成mysql slave,收集binlog做解析,然后再进行后续同步操作。所以我们的准备工作必须要求MySQL开启binlog日志:

[mysqld]
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复

授权 canal 链接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant

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

当然也可以不用新增账号,直接使用root账号,为了方便快捷,我下面的案例就是使用root账号的哦,当然这不符合开发规范,root权限账号一般人不能用的~

 https://zhuanlan.zhihu.com/p/655011270?utm_id=0

 

 

 

 

 

 

 

 

 

 

canal的工作原理:

原理相对比较简单:

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

架构

说明:

server代表一个canal运行实例,对应于一个jvm
instance对应于一个数据队列 (1个server对应1..n个instance)
instance代表了一个实际运行的数据队列,包括了EventPaser,EventSink,EventStore等组件。

instance模块:

eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
eventStore (数据存储)
metaManager (增量订阅&消费信息管理器)

简单点说:

mysql的binlog是多文件存储,定位一个LogEvent需要通过binlog filename + binlog position,进行定位
mysql的binlog数据格式,按照生成的方式,主要分为:statement-based、row-based、mixed。
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.00 sec)
目前canal支持所有模式的增量订阅(但配合同步时,因为statement只有sql,没有数据,无法获取原始的变更日志,所以一般建议为ROW模式)

EventParser设计
大致过程:
整个parser过程大致可分为几步:

Connection获取上一次解析成功的位置 (如果第一次启动,则获取初始指定的位置或者是当前数据库的binlog位点)
Connection建立链接,发送BINLOG_DUMP指令
// 0. write command number
// 1. write 4 bytes bin-log position to start at
// 2. write 2 bytes bin-log flags
// 3. write 4 bytes server id of the slave
// 4. write bin-log file name
Mysql开始推送Binaly Log
接收到的Binaly Log的通过Binlog parser进行协议解析,补充一些特定信息
// 补充字段名字,字段类型,主键信息,unsigned类型处理
传递给EventSink模块进行数据存储,是一个阻塞操作,直到存储成功
存储成功后,定时记录Binaly Log位置

 

EventSink设计
说明:
数据过滤:支持通配符的过滤模式,表名,字段内容等
数据路由/分发:解决1:n (1个parser对应多个store的模式)
数据归并:解决n:1 (多个parser对应1个store)
数据加工:在进入store之前进行额外的处理,比如join

EventStore设计
1. 目前仅实现了Memory内存模式,后续计划增加本地file存储,mixed混合模式
2. 借鉴了Disruptor的RingBuffer的实现思路

 

HA(High Available)机制设计
canal的ha分为两部分,canal server和canal client分别有对应的ha实现

canal server: 为了减少对mysql dump的请求,不同server上的instance要求同一时间只能有一个处于running,其他的处于standby状态.
canal client: 为了保证有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接收无法保证有序。

整个HA机制的控制主要是依赖了zookeeper的几个特性,watcher和EPHEMERAL节点(和session生命周期绑定)。

Canal Server:

大致步骤:
(1)canal server要启动某个canal instance时都先向zookeeper进行一次尝试启动判断 (实现:创建EPHEMERAL节点,谁创建成功就允许谁启动,类似一个分布式锁)
(2)创建zookeeper节点成功后,对应的canal server就启动对应的canal instance,没有创建成功的canal instance就会处于standby状态
(3)一旦zookeeper发现canal server A创建的节点消失后,立即通知其他的canal server再次进行步骤1的操作,重新选出一个canal server启动instance.

Canal Client:
canal client每次进行connect时,会首先向zookeeper询问当前是谁启动了canal instance,然后和其建立链接,一旦链接不可用,会重新尝试connect.
Canal Client的方式和canal server方式类似,也是利用zookeeper的抢占EPHEMERAL节点的方式进行控制.


ephemeral 短暂的; 瞬息的

https://github.com/alibaba/canal/wiki/%E7%AE%80%E4%BB%8B#canal%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86

binlog被删除的问题
aliyun RDS有自己的binlog日志清理策略,这个策略相比于用户自建mysql会更加激进,默认应该是18小时就会清理binlog并上传到oss上,可以在页面上进行调整,或者业务可以通过oss下载更早的binlog

canal中配置ak,sk的地方:

# aliyun ak/sk , support rds/mq
canal.aliyun.accessKey =
canal.aliyun.secretKey =

注意点:
相比于普通的mysql配置,多了rds oss binlog所需要的aliyun ak/sk/实例id等相关信息(如果不需要在本地binlog超过18小时被清理后自动下载oss上的binlog,可以忽略该值)

 

canal.instance.rds.accesskey aliyun账号的ak信息 (如果不需要在本地binlog超过18小时被清理后自动下载oss上的binlog,可以忽略该值)
canal.instance.rds.secretkey aliyun账号的sk信息(如果不需要在本地binlog超过18小时被清理后自动下载oss上的binlog,可以忽略该值)
canal.instance.rds.instanceId aliyun rds对应的实例id信息(如果不需要在本地binlog超过18小时被清理后自动下载oss上的binlog,可以忽略该值)

https://github.com/alibaba/canal/wiki/aliyun-RDS-QuickStart

 

 

otter中配置ak,sk的地方:

 

 

公有云API的认证方式:AK/SK 简介

1 公有云API的认证方式
一般有一夏几种认证方式:
    Token认证
    AK/SK认证
    RSA非对称加密方式
    下面主要介绍AK/SK

1 AK/SK
1.1 AK/SK 原理

云主机需要通过使用Access Key Id / Secret Access Key加密的方法来验证某个请求的发送者身份。
Access Key Id(AK)用于标示用户,Secret Access Key(SK)是用户用于加密认证字符串和云厂商用来验证认证字符串的密钥,其中SK必须保密。 AK/SK原理使用对称加解密
1.2 AK/SK使用机制

云主机接收到用户的请求后,系统将使用AK对应的相同的SK和同样的认证机制生成认证字符串,并与用户请求中包含的认证字符串进行比对。如果认证字符串相同,系统认为用户拥有指定的操作权限,并执行相关操作;如果认证字符串不同,系统将忽略该操作并返回错误码。
1.3 流程
    判断用户请求中是否包含Authorization认证字符串。如果包含认证字符串,则执行下一步操作。
    基于HTTP请求信息,使用相同的算法,生成Signature字符串。
    使用服务器生成的Signature字符串与用户提供的字符串进行比对,如果内容不一致,则认为认证失败,拒绝该请求;如果内容一致,则表示认证成功,系统将按照用户的请求内容进行操作。

原理:
  客户端:
    1. 构建http请求(包含 access key);
    2. 使用请求内容和 使用secret access key计算的签名(signature);
    3. 发送请求到服务端。
服务端:
    1. 根据发送的access key 查找数据库得到对应的secret-key;
    2. 使用同样的算法将请求内容和 secret-key一起计算签名(signature),与客户端步骤2相同;
    3. 对比用户发送的签名和服务端计算的签名,两者相同则认证通过,否则失败。
Token
Token认证
使用Token认证方式完成认证鉴权时,用户首先需要获取token,在调用接口时增加“X-XXX-Token”到业务接口请求消息头中。
流程
    发送请求,获取IAM的Endpoint及消息体中的区域名称。
    获取Token。请求响应成功后在响应消息头中包含的“X-YYY-Token”的值即为Token值。
    调用业务接口,在请求消息头中增加“X-XXX-Token”,取值为2中获取的Token。

https://blog.csdn.net/makenothing/java/article/details/81158481

 

2020-05-29 17:06:25.217 ERROR [bootstrap,,,] 12 --- [37.225.101:2181] - [] org.I0Itec.zkclient.ZkEventThread        : Error handling event ZkEvent[Data of /otter/canal/destinations/zaf-security-admin/1001/running changed sent to com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor$1@731b42f4]
com.alibaba.otter.canal.protocol.exception.CanalClientException: something goes wrong in initRunning method. 
    at com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor.initRunning(ClientRunningMonitor.java:156) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor$1.handleDataDeleted(ClientRunningMonitor.java:72) ~[canal.client-1.1.3.jar!/:na]
    at org.I0Itec.zkclient.ZkClient$9.run(ZkClient.java:825) ~[zkclient-0.10.jar!/:na]
    at org.I0Itec.zkclient.ZkEventThread.run(ZkEventThread.java:72) ~[zkclient-0.10.jar!/:na]
Caused by: com.alibaba.otter.canal.protocol.exception.CanalClientException: java.io.IOException: end of stream when reading header
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.doConnect(SimpleCanalConnector.java:189) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.access$000(SimpleCanalConnector.java:50) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector$1.processActiveEnter(SimpleCanalConnector.java:422) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor.processActiveEnter(ClientRunningMonitor.java:221) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.running.ClientRunningMonitor.initRunning(ClientRunningMonitor.java:123) ~[canal.client-1.1.3.jar!/:na]
    ... 3 common frames omitted
Caused by: java.io.IOException: end of stream when reading header
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.read(SimpleCanalConnector.java:404) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.readNextPacket(SimpleCanalConnector.java:392) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.readNextPacket(SimpleCanalConnector.java:376) ~[canal.client-1.1.3.jar!/:na]
    at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.doConnect(SimpleCanalConnector.java:151) ~[canal.client-1.1.3.jar!/:na]
    ... 7 common frames omitted

问题一:

ERROR c.a.otter.canal.parse.inbound.mysql.MysqlEventParser - dump address /192.168.1.50:3306 has an error, retrying. caused by

com.alibaba.otter.canal.parse.exception.CanalParseException: can't find start position for example

原因:meta.dat 中保存的位点信息和数据库的位点信息不一致【mysql目前已经没有保存这个位点了】;导致canal抓取不到数据库的动作;

解决方案:删除meta.dat删除,再重启canal,问题解决;

集群操作:进入canal对应的zookeeper集群下,删除节点/otter/canal/destinations/xxxxx/1001/cursor ;重启canal即可恢复;

 

问题二:

java.lang.OutOfMemoryError: Java heap space
 

canal消费端挂了太久,在zk对应conf下节点的

/otter/canal/destinations/test_db/1001/cursor 位点信息是很早以前,导致重启canal时,从很早以前的位点开始消费,导致canal服务器内存爆掉

监听数据库变更,只有TransactionBegin/TransactionEnd,没有拿到数据的EventType;

原因可能是canal.instance.filter.black.regex=.*\\..*导致,改canal.instance.filter.black.regex=再重启试试;

mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\) 
常见例子:
1.  所有表:.*   or  .*\\..*
2.  canal schema下所有表: canal\\..*
3.  canal下的以canal打头的表:canal\\.canal.*
4.  canal schema下的一张表:canal.test1
5.  多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
注意:此过滤条件只针对row模式的数据有效(ps. mixed/statement因为不解析sql,所以无法准确提取tableName进行过滤)

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



问题三:

ERROR com.alibaba.otter.canal.common.alarm.LogAlarmHandler - destination:fdyb_db[com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: com.google.common.collect.ComputationException: com.alibaba.otter.canal.parse.exception.CanalParseException: fetch failed by table meta:`mysql`.`pds_4490277`
Caused by: com.google.common.collect.ComputationException: com.alibaba.otter.canal.parse.exception.CanalParseException: fetch failed by table meta:`mysql`.`pds_4490277`

Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: fetch failed by table meta:`mysql`.`pds_4490277`
Caused by: java.io.IOException: ErrorPacket [errorNumber=1142, fieldCount=-1, message=SELECT command denied to user 'cy_canal'@'11.217.0.224' for table 'pds_4490277', sqlState=42000, sqlStateMarker=#]
with command: desc `mysql`.`pds_4490277`

分析:mysql系统表权限较高,canal读该表的binlog失败,位点无法移动

解决:将配置项中黑名单加上mysql下的所有表:canal.instance.filter.black.regex = mysql\\..* ,修改后canal集群不需要重启即可恢复;

其它注意点:检查下CanalConnector是否调用subscribe(filter)方法;
有的话,filter需要和instance.properties的canal.instance.filter.regex一致,否则subscribe的filter会覆盖instance的配置,如果subscribe的filter是.*\\..*,那么相当于你消费了所有的更新数据。

 

问题四:

现象:数据库修改后,canal应用感知不到binlog,数据无法正常消费处理;

定位:
1.查看canal服务器,canal应用,zk服务器的日志,确认无异常;
2.查看mysql,es服务器,无异常,
3.查看canal服务器,canal应用配置项,发现canal服务器的canal.properties有问题;

原因:canal.properties中配置了canal.ip和canal.zkServers,如果是zk集群模式下的canal配置了canal.ip,则会优先按IP连接canal服务器,从而让zk功能失效,位点文件则会保存到本地
一旦本地位点文件出现问题,各方无错误日志,问题就很难排查;

解决:将canal.ip配置项置为空,关掉canal服务器,canal应用,删除zk上的节点,重启canal服务器,canal应用,问题解决;
https://blog.csdn.net/my201110lc/article/details/77885720


问题五:

2020-10-26 14:02:52.510 [destination = ddbuat , address = hym-mysql-uat-cluster-private.rwlb.rds.aliyuncs.com/10.10.3.3:3306 , EventParser] WARN  c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---> find start position successfully, EntryPosition[included=false,journalName=mysql-bin.006381,position=356075532,serverId=14457169,gtid=,timestamp=1603682791000] cost : 9ms , the next step is binlog dump
2020-10-26 14:02:52.520 [MultiStageCoprocessor-Parser-ddbuat-6] ERROR com.alibaba.otter.canal.common.utils.NamedThreadFactory - from MultiStageCoprocessor-Parser-ddbuat-6
com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:db.hymnews,33 vs 25
2020-10-26 14:02:52.523 [destination = ddbuat , address = hym-mysql-uat-cluster-private.rwlb.rds.aliyuncs.com/10.10.3.3:3306 , EventParser] ERROR c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - dump address hym-mysql-uat-cluster-private.rwlb.rds.aliyuncs.com/10.10.3.3:3306 has an error, retrying. caused by 
com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:db.hymnews,33 vs 25
2020-10-26 14:02:52.525 [MultiStageCoprocessor-Parser-ddbuat-11] ERROR com.alibaba.otter.canal.common.utils.NamedThreadFactory - from MultiStageCoprocessor-Parser-ddbuat-11
com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:db.hymnews,33 vs 25
2020-10-26 14:02:52.525 [destination = ddbuat , address = hym-mysql-uat-cluster-private.rwlb.rds.aliyuncs.com/10.10.3.3:3306 , EventParser] ERROR com.alibaba.otter.canal.common.alarm.LogAlarmHandler - destination:ddbuat[com.alibaba.otter.canal.parse.exception.CanalParseException: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: parse row data failed.
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:db.hymnews,33 vs 25

原因:开启了tsdb,存了db表结构快照
解决办法:
把存的快照数据删除,canal会把当前的库的表结构重新存一份,就不会出现上面的报错了

delete from meta_snapshot where destination='报错的destination'

指定的库:

# table meta tsdb info
canal.instance.tsdb.enable=true
canal.instance.tsdb.url=jdbc:mysql://rm-xxx.mysql.rds.aliyuncs.com:3306/canal_tsdb?useSSL=false
canal.instance.tsdb.dbUsername=admin
canal.instance.tsdb.dbPassword=pwd
# dump snapshot interval, default 24 hour
canal.instance.tsdb.snapshot.interval = 24
# purge snapshot expire , default 360 hour(15 days)
canal.instance.tsdb.snapshot.expire = 360
canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml

 



canal在zk上的存储路径:

/**
 * 存储结构:
 * 
 * <pre>
 * /otter
 *    canal
 *      cluster
 *      destinations
 *        dest1
 *          running (EPHEMERAL) 
 *          cluster
 *          client1
 *            running (EPHEMERAL)
 *            cluster
 *            filter
 *            cursor
 *            mark
 *              1
 *              2
 *              3
 * </pre>
 * 
 * @author zebin.xuzb @ 2012-6-21
 * @version 1.0.0
 */
public class ZookeeperPathUtils {

    public static final String ZOOKEEPER_SEPARATOR                          = "/";

    public static final String OTTER_ROOT_NODE                              = ZOOKEEPER_SEPARATOR + "otter";

    public static final String CANAL_ROOT_NODE                              = OTTER_ROOT_NODE + ZOOKEEPER_SEPARATOR
                                                                              + "canal";

    public static final String DESTINATION_ROOT_NODE                        = CANAL_ROOT_NODE + ZOOKEEPER_SEPARATOR
                                                                              + "destinations";

    public static final String FILTER_NODE                                  = "filter";

    public static final String BATCH_MARK_NODE                              = "mark";

    public static final String PARSE_NODE                                   = "parse";

    public static final String CURSOR_NODE                                  = "cursor";

    public static final String RUNNING_NODE                                 = "running";

    public static final String CLUSTER_NODE                                 = "cluster";

    public static final String DESTINATION_NODE                             = DESTINATION_ROOT_NODE
                                                                              + ZOOKEEPER_SEPARATOR + "{0}";

    public static final String DESTINATION_PARSE_NODE                       = DESTINATION_NODE + ZOOKEEPER_SEPARATOR
                                                                              + PARSE_NODE;

    public static final String DESTINATION_CLIENTID_NODE                    = DESTINATION_NODE + ZOOKEEPER_SEPARATOR
                                                                              + "{1}";

    public static final String DESTINATION_CURSOR_NODE                      = DESTINATION_CLIENTID_NODE
                                                                              + ZOOKEEPER_SEPARATOR + CURSOR_NODE;

    public static final String DESTINATION_CLIENTID_FILTER_NODE             = DESTINATION_CLIENTID_NODE
                                                                              + ZOOKEEPER_SEPARATOR + FILTER_NODE;

    public static final String DESTINATION_CLIENTID_BATCH_MARK_NODE         = DESTINATION_CLIENTID_NODE
                                                                              + ZOOKEEPER_SEPARATOR + BATCH_MARK_NODE;

    public static final String DESTINATION_CLIENTID_BATCH_MARK_WITH_ID_PATH = DESTINATION_CLIENTID_BATCH_MARK_NODE
                                                                              + ZOOKEEPER_SEPARATOR + "{2}";

    /**
     * 服务端当前正在提供服务的running节点
     */
    public static final String DESTINATION_RUNNING_NODE                     = DESTINATION_NODE + ZOOKEEPER_SEPARATOR
                                                                              + RUNNING_NODE;

    /**
     * 客户端当前正在工作的running节点
     */
    public static final String DESTINATION_CLIENTID_RUNNING_NODE            = DESTINATION_CLIENTID_NODE
                                                                              + ZOOKEEPER_SEPARATOR + RUNNING_NODE;

    /**
     * 整个canal server的集群列表
     */
    public static final String CANAL_CLUSTER_ROOT_NODE                      = CANAL_ROOT_NODE + ZOOKEEPER_SEPARATOR
                                                                              + CLUSTER_NODE;

    public static final String CANAL_CLUSTER_NODE                           = CANAL_CLUSTER_ROOT_NODE
                                                                              + ZOOKEEPER_SEPARATOR + "{0}";

    /**
     * 针对某个destination的工作的集群列表
     */
    public static final String DESTINATION_CLUSTER_ROOT                     = DESTINATION_NODE + ZOOKEEPER_SEPARATOR
                                                                              + CLUSTER_NODE;
    public static final String DESTINATION_CLUSTER_NODE                     = DESTINATION_CLUSTER_ROOT
                                                                              + ZOOKEEPER_SEPARATOR + "{1}";

    public static String getDestinationPath(String destinationName) {
        return MessageFormat.format(DESTINATION_NODE, destinationName);
    }

    public static String getClientIdNodePath(String destinationName, short clientId) {
        return MessageFormat.format(DESTINATION_CLIENTID_NODE, destinationName, String.valueOf(clientId));
    }

    public static String getFilterPath(String destinationName, short clientId) {
        return MessageFormat.format(DESTINATION_CLIENTID_FILTER_NODE, destinationName, String.valueOf(clientId));
    }

    public static String getBatchMarkPath(String destinationName, short clientId) {
        return MessageFormat.format(DESTINATION_CLIENTID_BATCH_MARK_NODE, destinationName, String.valueOf(clientId));
    }

    public static String getBatchMarkWithIdPath(String destinationName, short clientId, Long batchId) {
        return MessageFormat.format(DESTINATION_CLIENTID_BATCH_MARK_WITH_ID_PATH,
            destinationName,
            String.valueOf(clientId),
            getBatchMarkNode(batchId));
    }

    public static String getCursorPath(String destination, short clientId) {
        return MessageFormat.format(DESTINATION_CURSOR_NODE, destination, String.valueOf(clientId));
    }

    public static String getCanalClusterNode(String node) {
        return MessageFormat.format(CANAL_CLUSTER_NODE, node);
    }

    /**
     * 服务端当前正在提供服务的running节点
     */
    public static String getDestinationServerRunning(String destination) {
        return MessageFormat.format(DESTINATION_RUNNING_NODE, destination);
    }

    /**
     * 客户端当前正在工作的running节点
     */
    public static String getDestinationClientRunning(String destination, short clientId) {
        return MessageFormat.format(DESTINATION_CLIENTID_RUNNING_NODE, destination, String.valueOf(clientId));
    }

    public static String getDestinationClusterNode(String destination, String node) {
        return MessageFormat.format(DESTINATION_CLUSTER_NODE, destination, node);
    }

    public static String getDestinationClusterRoot(String destination) {
        return MessageFormat.format(DESTINATION_CLUSTER_ROOT, destination);
    }

    public static String getParsePath(String destination) {
        return MessageFormat.format(DESTINATION_PARSE_NODE, destination);
    }

    /**
     * 将batchNode转换为Long
     */
    public static short getClientId(String clientNode) {
        return Short.valueOf(clientNode);
    }

    /**
     * 将batchNode转换为Long
     */
    public static long getBatchMarkId(String batchMarkNode) {
        return Long.valueOf(batchMarkNode);
    }

    /**
     * 将batchId转化为zookeeper中的node名称
     */
    public static String getBatchMarkNode(Long batchId) {
        return StringUtils.leftPad(String.valueOf(batchId.intValue()), 10, '0');
    }
}

com.alibaba.otter.canal.common.zookeeper.ZookeeperPathUtils

 

posted @ 2020-05-15 11:47  沧海一滴  阅读(1262)  评论(0编辑  收藏  举报