基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_reading/canal

instance模块比较简单,我们重点了解以下几个问题

  • instance配置模式有哪几种,如何根据配置创建instance?
  • 远端配置如何覆盖本地配置的?
  • instance实例内部有哪些组件?

1.基本结构

instance模块下面也分为三个子模块,core、manager、spring。

其中,core是instance的核心逻辑 。

而manager和spring只是两种不同的instance配置读取方式,manager通过http请求读取admin的配置,spring通过配置文件的方式读取。

主要控制逻辑我们在deployer模块源码分析中提到过,就是在CanalController类 的instanceGenerator,配置参数是canal.instance.global.mode

  • 根据destination创建config
  • 如果canal.instance.global.mode = manager,就使用PlainCanalInstanceGenerator
  • 如果canal.instance.global.mode = spring,就使用SpringCanalInstanceGenerator

源码如下

2.core子模块

代码不多,就两个接口,两个类。

2.1 CanalInstanceGenerator接口

这个接口只有一个方法

具体实现就是开头提到的两种,PlainCanalInstanceGenerator和SpringCanalInstanceGenerator,分别在manager子模块和spring子模块中实现。

具体选择就是开头的那个canalController里面根据canal.instance.global.mode来选择。

2.2 CanalInstance接口

先看一张官方文档的图,这个前面的文章已经分析过了。

server代表一个canal-server运行实例,对应于一个jvm。server内部可以有多个instance。

Instance内部有4个主要组件:

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

在这个接口中,就定义了获取4个组件的方法,以及新版本增加的mqProducer的配置信息(mqProducer在server模块解析中介绍过了,可以回头去看看)

我们简单看下4个组件接口的各个实现类。

CanalEventParser接口实现类(paser模块):

  • MysqlEventParser:伪装成单个mysql实例的slave解析binglog日志
  • GroupEventParser:伪装成多个mysql实例的slave解析binglog日志。内部维护了多个CanalEventParser,组合多个EventParser进行合并处理,group只是作为一个delegate处理。主要应用场景是分库分表:比如一个大表拆分了4个库,位于不同的mysql实例上,正常情况下,我们需要配置四个CanalInstance。对应的,业务上要消费数据时,需要启动4个客户端,分别链接4个instance实例。为了方便业务使用,此时我们可以让CanalInstance引用一个GroupEventParser,由GroupEventParser内部维护4个MysqlEventParser去4个不同的mysql实例去拉取binlog,最终合并到一起。此时业务只需要启动1个客户端,链接这个CanalInstance即可
  • LocalBinlogEventParser:解析本地的mysql binlog。例如将mysql的binlog文件拷贝到canal的机器上进行解析。
  • RdsLocalBinlogEventParser:基于阿里云rds的binlog备份文件复制,下载到本地后进行本地的binlog解析。

CanalEventSink接口实现类(sink模块):

  • EntryEventSink:普通的单个parser的sink操作,进行数据过滤,加工,分发
  • GroupEventSink:用于分库分表的场景,对应GroupEventParser的数据解析,然后实现基于归并排序的sink处理

CanalEventStore接口实现类(store模块):

  • MemoryEventStoreWithBuffer:基于内存实现存储store

CanalMetaManager(meta模块):

  • ZooKeeperMetaManager:将元数据存存储到zk中
  • MemoryMetaManager:将元数据存储到内存中
  • MixedMetaManager:组合memory + zookeeper的使用模式
  • PeriodMixedMetaManager:基于定时刷新的策略的mixed实现
  • FileMixedMetaManager:先写内存,然后定时刷新数据到File

关于这些实现的具体细节,我们在相应模块的源码分析时,进行讲解。目前只需要知道,一些组件有多种实现,因此组合工作方式有多种。

2.3 AbstractCanalInstance类

AbstractCanalInstance是canalInstance的抽象类,维护了相关组件的引用

这个抽象类有两个实现,CanalInstanceWithManager 和 CanalInstanceWithSpring。

AbstractCanalInstance的初始化过程都是在实现类中完成的。

如果选择admin控制模式,那就是在CanalInstanceWithManager中完成,如果是spring模式,就在CanalInstanceWithSpring中完成。

但是它们的初始化过程并不是在这里完成的,如果选择admin控制模式,那就是在CanalInstanceWithManager中完成,如果是spring模式,就在CanalInstanceWithSpring中完成。

这里有个小发现:

仔细看下实际代码调用我们发现,CanalInstanceWithManager是给ManagerCanalInstanceGenerator使用的,而这个generator实际上没有被使用到。如果使用admin模式,本文开头我们就看到了,使用了PlainCanalInstanceGenerator。PlainCanalInstanceGenerator里面的generate方法实现,其实跟SpringCanalInstanceGenerator差不多。就是从远端admin拉到配置,然后替换系统变量,然后再从spring的beanfactory中构建具体的实例。

2.3.1 subscribeChange() 方法

AbstractCanalInstance类实现了CanalInstance接口的subscribeChange方法。

我们看到,如果订阅关系发生变化,就做一些操作,这里看的话,主要就是更新了一下filter。

filter规定了需要订阅哪些库,哪些表。

2.3.2 start() 方法

启动没什么特别的逻辑,就是按照顺序依次启动各个组件。

顺序为 metaManager -> alarmHandler -> eventStore -> eventSink -> eventParser。

启动顺序主要跟依赖关系有关,元信息相关的管理跟所有都有关,所以metaManager最先启动,其他的按照彼此之间的关系一一启动。

这里我们发现,在启动eventParser的时候做了特殊处理,分别是beforeStartEventParser 和 afterStartEventParser。后文我们专门讲一下。

2.3.3 stop()方法

stop也没什么特殊的,就是依次关闭各个组件。

关闭的顺序就是start的逆过程。

这里就不贴代码了。

2.3.4 eventParserr的特殊处理

在start和stop方法中的eventParser前后都有特殊的处理,start的beforeStartEventParser 和 afterStartEventParser,Stop的beforeStopEventParser 和 afterStopEventParser。

这个其实跟eventParser的设计有关。

EventParser 设计

  • 每个EventParser都会关联两个内部组件
  • CanalLogPositionManager : 记录binlog 最后一次解析成功位置信息,主要是描述下一次canal启动的位点 CanalHAController: 控制 EventParser 的链接主机管理,判断当前该链接哪个mysql数据库

所以这两个beforexxx、afterxxxx方法做的主要是CanalLogPositionManager和CanalHAController的启停工作。

2.3.5 AbstractCanalInstance类 总结

可以看到AbstractCanalInstance除了负责启动和停止其内部组件,就没有其他工作了。

eventParser在AbstractCanalInstance中启动后,就会自行开启多线程任务dump数据,通过eventSink投递给eventStore。

而对eventStore的操作逻辑,实际上都是在CanalServerWithEmbedded中完成的,我们可以回顾一下CanalServerWithEmbedded中 getWithoutAck( ) 的相关逻辑。

包括:

  • 根据clientIdentity的destination获取对应的instance
  • 获取到流式数据中的最后一批获取的位置positionRanges(跟batchId有关联,就是上面那个map里面的)
  • 从cananlEventStore里面获取binlog,转化为event。一般是从最后的一个batchId位置开始,如果之前没有batchId,那么就从cursor记录的消费位点开始;如果cursor为空,那只能从eventStore的第一条消息开始。(这里几个位置关系再想一想,跟ack有关,画个图)
  • event转化为entry,并生成新的batchId,组合成message返回给客户端

所以,其实这里只是简单的启动和停止,组件的交互逻辑是在CanalServerWithEmbedded中get出instance的各个组件来进行实现的。

3.spring模块

前面提到了,PlainCanalInstanceGenerator里面的generate方法实现,其实跟SpringCanalInstanceGenerator差不多。就是从远端admin拉到配置,然后替换系统变量,然后再从spring的beanfactory中构建具体的实例。

所以我们重点关注spring子模块的配置方式即可。

就下面四个类

3.1 CanalInstanceWithSpring类

基于spring容器启动canal实例,方便独立于manager启动。

继承了AbstractCanalInstance,其实就是一系列组件的setter方法,就不贴源码了。

具体配置是基于spring的xml来做的.

当我们配置加载方式为spring时,创建的CanalInstance实例类型都是CanalInstanceWithSpring。canal将会寻找本地的spring配置文件来创建instance实例。canal默认提供了以下几种spring配置文件:

  • spring/memory-instance.xml
  • spring/file-instance.xml
  • spring/default-instance.xml
  • spring/group-instance.xml

四个配置文件中,对CanalInstanceWithSpring都采用了同样的配置方式:

当然,具体每个组件的ref在不同配置文件中有所不同。

最主要的就是metaManager 和eventParser 这两个配置有所不同,可能在内存、文件或zk进行存储。

eventStore 、和eventSink 定义都是相同的,eventStore目前的开源版本中eventStore只有一种基于内存的实现,eventSink其作用是eventParser和eventStore的连接器,进行数据过滤,加工,分发的工作。不涉及存储,也就没有必要针对内存、file、或者zk进行区分。

3.2 SpringCanalInstanceGenerator类

这个是具体创建instance的逻辑。

顺便看下PlainCanalInstanceGenerator里面的实现,就是多了从远端拉取配置,然后用PropertyPlaceholderConfigurer进行了变量替换,然后还是用beanFactory来获取实例。

com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer继承了org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,设置动态properties,替换掉本地properties。

4.总结

其实这个模块的东西比较少,没有什么特别复杂的逻辑。

我们来回顾下开头的几个问题

  • instance配置模式有哪几种,如何根据配置创建instance?

主要有基于spring和基于远端配置两种方式,前者的实现在,后者的实现在PlainCanalInstanceGenerator

  • 远端配置如何覆盖本地配置的?

PlainCanalInstanceGenerator中使用了spring的PropertyPlaceholderConfigurer来覆盖配置

  • instance实例内部有哪些组件?

包括了parser、sink、store、metamanager等组件,但是只负责了启动和停止逻辑,具体交互逻辑还是在CanalServerWithEmbedded中实现的。

都看到最后了,原创不易,点个关注,点个赞吧~

文章持续更新,可以微信搜索「阿丸笔记 」第一时间阅读,回复关键字【学习】有我准备的一线大厂面试资料。

知识碎片重新梳理,构建Java知识图谱:github.com/saigu/JavaK…(历史文章查阅非常方便)

「从零单排canal 06」 instance模块源码解析的更多相关文章

  1. 「从零单排canal 05」 server模块源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  2. 「从零单排canal 07」 parser模块源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  3. 「从零单排canal 04」 启动模块deployer源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  4. 「从零单排canal 01」 canal 10分钟入门(基于1.1.4版本)

    1.简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Servi ...

  5. 「从零单排canal 03」 canal源码分析大纲

    在前面两篇中,我们从基本概念理解了canal是一个什么项目,能应用于什么场景,然后通过一个demo体验,有了基本的体感和认识. 从这一篇开始,我们将从源码入手,深入学习canal的实现方式.了解can ...

  6. 「从零单排canal 02」canal集群版 + admin控制台 最新搭建姿势(基于1.1.4版本)

    canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Service)的开 ...

  7. 「从零单排HBase 06」你必须知道的HBase最佳实践

    前面,我们已经打下了很多关于HBase的理论基础,今天,我们主要聊聊在实际开发使用HBase中,需要关注的一些最佳实践经验. 1.Schema设计七大原则 1)每个region的大小应该控制在10G到 ...

  8. Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  9. 「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践

    Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs对HBase数据进行增删改查,构建二级索引.当然,开源产品嘛,自然需要注意“避坑”啦,阿丸会把使用方式和最佳实践都告 ...

随机推荐

  1. .Net Core 集成ExceptionLess分布式日志框架之本地化部署

    前言 公司目前使用的项目中关于日志记录这块,之前一直都是使用的Log4net 存放于后台文件中的,对于异常错误啊,或者需要查看一些详情错误的时候感觉很不方便,要到服务器上去打开日志文件检索错误,降低了 ...

  2. JS控制滚动条的位置

    转载▼http://blog.sina.com.cn/s/blog_4481a3460100rwwu.html     JS控制滚动条的位置:window.scrollTo(x,y); 竖向滚动条置顶 ...

  3. ADAS感知开发问题

    ADAS感知开发问题 1. 雨天相机 问题:雨天相机目标识别不稳.出现目标时断时续的情况 对策: 增加单雷达生成功能.当单雷达目标置信度高时直接由雷达生成目标. 2. 相机震动目标位置突变 问题 :相 ...

  4. Java基础Day08(多线程)

    多线程 1. 线程 1.1 什么是线程: 程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列或者说是进程的子任务) 多线程执行时,在栈内存中,每一个执行线程都有自己所属的栈内存空间.进行 ...

  5. 使用 Egg + Vue 的第一个线上小产品——远程工作职位信息收集站点 yuancheng.works

    小插曲 开始很纠结,买了一个 yuancheng.works 域名会不会冒犯到 yuancheng.work 站长. 还在群里咨询了 @Phodal 等前辈.重新搞一个新域名,yuancheng.wo ...

  6. css文字不透明度怎么设置?

    在css中有很多好看的样式都可以实现,css设置出来的样式让整个网页看起来也会非常美观,今天的这篇文章就给大家来介绍一下在css中怎么设置文字的透明度,让你的文字在网页中看起来是透明的. CSS设置透 ...

  7. 切忌一步到位,谈谈DevOps实施落地

    2020年6月19日,由云计算开源产业联盟指导,高效运维社区和 DevOps 时代社区联合举办的GNSEC 2020线上峰会圆满举办.BoCloud博云参加了本次峰会并分享了博云帮助客户实施DevOp ...

  8. day49 数据库终章

    目录 一.pymysql补充 二.数据库补充 1 视图(了解) 2 触发器(了解) 3 事务 4 存储过程(了解) 5 函数 6 流程控制 7 索引 8 b+树 9 聚集索引(primary key) ...

  9. 利用docker部署elk交换机日志分析

    今天我们来聊一下利用docker部署elk日志分析系统,这里解析一下elk是啥东西.elk分别是Elasticsearch,Logstash和Kibana的首字母缩写. Elasticsearch是一 ...

  10. Tomcat更改错误页面指向,改变404,500错误页面

    在公司工作了一段时间,也被安排做了一个App,而且后台也是我来写和布置的,由于一次安全检查,需要我把tomcat默认页(管理页面)关闭,于是我只能进行默认指向变更,但是后面我又想到要是用户输入不存在的 ...