1.简单回顾

在binlog日志解析开源工具canal源码浅析(1):server模块一文中,我们主要学习了canal的server模块,关注点主要是如何实现一个canal-server来负责响应client端的请求。本节将继续本着自顶向下的分析方法来理解源码。

在server端是维护着多个instance来负责响应client的请求,也就是说本质上是通过各个instance来负责和client的交互的具体内容。一个instance和一个client交互的过程即为一个会话(类似的设计理念很多地方可以看到,例如oracle数据库响应用户的查询请求也是相同的原理)。instance与client交互时涉及的请求方法有connect,subscribe.ack,rollback等。这样的关系可以用下图来表示。

应该还记得CanalServerWithEmbedded这个类。在分析server的源码时,我们并没有引出instance的概念,而是说该类实现了ack,subscribe等session所需要的方法。实际上在这个类中则是通过获取instance对象及其方法,来完成连接订阅等会话功能的。看下CanalServerWithEmbedded中的ack和subscribe的实现即可知道。除了session所需要的功能方法,其他例如解析位点以及一些元数据的获取都要通过canalInstance.

至此,应该知道了我们今天讲的instance在canal中的定位了。那么废话不多说,我们开始说主要内容。

2.canal-instance基本结构

canal-instance的主要功能是两点:

  1. 加载配置
  2. 按照配置实现session所需要的相关方法

基本结构如下图所示:

canal配置方式有两种:

  1. ManagerCanalInstanceGenerator: 基于manager管理的配置方式,目前alibaba内部配置使用这种方式。可以实现CanalConfigClient,连接各自的管理系统,即可完成接入。
  2. SpringCanalInstanceGenerator:基于本地spring xml的配置方式,目前开源版本已经自带该功能所有代码,建议使用

本节也主要围绕Spring xml的配置方式来分析相关的源码

canal-instance的包结构如下图所示,核心的共性的方法类都安排在core中。根据是采用spring还是manager的配置方法再继承core内的抽象类再做进一步的实现。

3.instance-core

instance-core内的类图如下所示:

canal项目中设计上普遍使用了这种面向接口的编程思想,通过利用JAVA的多态使得代码有更好的扩展性和较低的耦合度,这时可以学习的地方。在instance-core包中,我们可以看到设计了一个工厂接口CanalInstanceGenerator用于产生不同的具体工厂,在我们这里主要是spring和manager会有各自具体的工厂。在CanalInstance的设计上也是采用了接口和抽象类。这里之所以采用抽象类是因为instance有一些共性的东西可以抽象,例如生命周期控制、基本组件(EventParser,EventSink等)

3.1 CanalInstance接口和AbstractCanalInstance

CanalInstance接口,主要提供了一些构成instance组件的获取方法(抽象方法):

AbstractCanalInstance抽象类,相比于CanalInstance多了一些方法,这些方法可以理解为”拦截器“方法(和struts2的拦截器意思一样),主要在进行EventParser之前或者之后增加一些操作:

AbstractCanalInstance抽象类中给出了相关instance的构成组件,作为成员变量,解释如下:

  1. destination: 目标即这个instance实例对应的消费者名称。配置canal.properties中的canal.destinations = example即可对destination进行命名和定义
  2. MetaManager: 增量订阅&消费信息管理器。CanalMetaManager对象,主要负责实现订阅、解析位点的游标管理、解析数据批处理的操作。相关接口设计如下图:

  1. CanalAlarmHandler:负责给destination发送警报信息,根据管理instance的不同方式可以继承该接口实现
  2. CanalEventParser:数据源接入,模拟slave协议和master进行交互,协议解析
  3. CanalEventSink :Parser和Store链接器,进行数据过滤,加工,分发的工作
  4. CanalEventStore: 事件数据存储

这里比较重要的核心组件是EventParser,EventSink,EventStore,三者的关系可以用下图简单来表示。这三个组件的更进一步展开我们将在以后讨论。

4. CanalInstanceWithSpring

前面我们看了core包,了解了其下顶级接口和抽象类的设计。这小节就是看下用spring管理instance的实现方式。相关的类设计如下图:

主要构成是:
1.support包:扩展了spring的功能,使得其支持给XML中属性配置默认值的功能
2.SpringCanalInstanceGenerator: 根据destination的内容,从spring容器中的bean工厂获取所需要的CanalInstance实例对象

  1. CanalInstanceWithSpring: 看源码发现是一些列对Instance核心组件的set方法。使用Spring来管理对象,起到IoC和依赖注入的效果

这里可以看看的是support当中的类是如何扩展spring的功能的。
spring的配置文件主要4种:

  1. spring/memory-instance.xml
  2. spring/file-instance.xml
  3. spring/default-instance.xml
  4. spring/group-instance.xml 以上4种配置文件分别代表了4种不同的instance管理模式,具体查看:canal在mysql5.7上的使用测试 中的3.2节的说明

4.1 PropertyPlaceholderConfigurer

查看spring配置文件的内容,我们可以看到这样的写法:

value表示路径采用了${canal.instance.destination:}/XXX 这样的形式,PropertyPlaceholderConfigurer类扩展了Spring原来的PropertyPlaceholderConfigurer的功能,支持使用环境变量来表示路径,如果不存在系统环境变量,则直接用冒号后面的默认值来代替。 关于PropertyPlaceholderConfigurer的用法可以查看这篇博文

4.2 SocketAddressEditor

这个类继承spring的PropertyEditorSupport,主要用于对string字符串进行处理,然后用自定义的对象来装配bean。在canal中是通过解析“ip:socket”这样的字符串,然后装配成InetSocketAddress对象。了解PropertyEditorSupport可以查看博客资料1博客资料2

4.3 EnumPropertyEditor

之前运行程序有警告如下:
found through deprecated global PropertyEditorManager fallback - consider using a more isolated form of registration, e.g. on the BeanWrapper/BeanFactory!

看着不是很爽,就写了这个类消除了这个警告。

5. 总结

这次我们一起感受了下Instance模块的设计,又学习了不少优秀的设计思想,同时深化了对canal的理解。根据自顶向下的基本思想,下一次自然是开始研究instance下面的几个核心组件:EventParser,EventSink,EventStorel。此外,周边组件包括MetaManager,AlarmHandler也是要看看的。