前言
canal是模拟mysql从节点,从而可以和mysql主库进行binlog的摘取,并消费。
用户的数据都是写入主库Master,Master将数据写入到本地二进制日志binary log中。从库Slave启动一个IO线程(I/O Thread)从主从同步binlog,写入到本地的relay log中,同时slave还会启动一个SQL Thread,读取本地的relay log,写入到本地,从而实现数据同步。了解更多请参考《mysql的复制及备份》
Canal架构
canal是模块依赖图及详细模块如下所示:
- deployer:部署模块。通过该模块提供的CanalLauncher来解析配置文件并启动canalServer.
- server模块:canal服务器端。核心接口为CanalServer
- instance模块:一个server有多个instance。每个instance都会模拟成一个mysql实例的slave。instance模块有四个核心组成部分:parser模块、sink模块、store模块,meta模块。核心接口为CanalInstance
- parser模块:数据源接入,模拟slave协议和master进行交互,协议解析。parser模块依赖于dbsync、driver,filter模块。
- driver模块和dbsync模块:这两个模块实际上是parser模块的组件。事实上parser是通过driver模块与mysql建立连接,从而获取到binlog。由于原始的binlog都是二进制流,需要解析成对应的binlog事件,这些binlog事件对象都定义在dbsync模块中,dbsync 模块来自于淘宝的tddl。
- filter模块:用于过滤parser获取的数据
- sink模块:parser和store链接器,进行数据过滤,加工,分发的工作。核心接口为CanalEventSink
- store模块:数据存储。核心接口为CanalEventStore
- meta模块:增量订阅&消费信息管理器,核心接口为CanalMetaManager,主要用于记录canal消费到的mysql binlog的位置,
- client模块:canal的客户端。核心接口为CanalConnector
- protocol模块:client和server模块之间的通信协议
- example模块:提供client模块使用案例。
- common模块:主要是提供了一些公共的工具类和接口。
Driver模块
driver,顾名思义为驱动。熟悉jdbc编程的同学都知道,当项目中需要操作数据库(oracle、sqlserver、mysql等)时,都需要在项目中引入对应的数据库的驱动。以mysql为例,我们需要引入的是mysql-connector-java这个jar包,通过这个驱动包来与数据库进行通信。
那么为什么canal不使用mysql官方提供的驱动包,而要自己编写一个driver模块?原因在于mysql-connector-java驱动包只是实现了JDBC规范,方便我们在程序中对数据库中的数据进行增删改查。
对于获取并解析binlog日志这样的场景,mysql-connector-java并没有提供这样的功能。因此,canal编写了自己的driver模块,提供了基本的增删改查功能,并提供了直接获取原始binlog字节流的功能,其他模块在这个模块的基础上对binlog字节进行解析.
#可以获得当前的binlog位置
show master status
#查询最早的binlog位置
show binlog events limit 1
#主要用于判断MySQL复制同步状态
#是否下面2线程在运行,判断mysql是否向名提供binlog服务
#Slave_IO_Running线程:负责把主库的bin日志(Master_Log)内容,投递到从库的中继日志上(Relay_Log)
#Slave_SQL_Running线程:负责把中继日志上的语句在从库上执行一遍
show slave status
#用于查看binlog格式,值为STATEMENT,MIXED,ROW的一种
show variables like 'binlog_format'
meta模块
管理一些元数据信息包括:
- 订阅信息,即是否有消费着订阅binlog.
- 每个Client消费游标信息,即消费进度
- 每个Client消费批次信息,根据批次获取此批次的开始游标,结束游标
#将元数据存存储到zk中
ZooKeeperMetaManager
#将元数据存储到内存中
MemoryMetaManager
#组合memory + zookeeper的使用模式
MixedMetaManager:
#基于定时刷新的策略的mixed实现
PeriodMixedMetaManager
#先写内存,然后定时刷新数据到File
FileMixedMetaManager:
Parser模块
EventParser在启动之前
- CanalLogPositionManager
mysql在主从同步过程中,要求slave自己维护binlog的消费进度信息。在slave机器的data目录下,都会有一个master.info文件,这个文件的作用就是存储主库的消费binlog解析进度信息。 - CanalHAController
为了保持binlog消费的高可用,通常会配置,主备mysql的地址。当主库心跳丢失,Mysql会接取从库的binlog.
EventParser在启动之后
- 会更新一下eventParser中的filter
一个mysql实例中可能会有多个库,每个库里面又会有多个表,可能我们只是想订阅某个库中的部分表,这个时候就需要进行过滤。- eventFilter属性:使用配置项canal.instance.filter.regex的值进行白名单过滤。
- eventBlackFilter属性:使用配置项canal.instance.filter.black.regex进行黑名单过滤。
EventParser启动后,会创建一个工作线程跑(因为binlog是顺序结构的)
- 获取最后的位置信息
- CanalLogPositionManager中历史消费信息
- 如果1没有,获取我们配置文件指定binlogName + offest
- 如果2没有,使用“show master status”获取Mysql的最新位置
- 根据获取的位置信息,在binlog中向上查找最新事务的位置(以事务维度消费,保证数据一致性)
- 构造SinkFunction
- 利用Driver的fetcher拉取数据
- 利用dbsync模块解析数据
- 把解析好的数据向SinkFunction导出
- SinkFunction中对数据进行filter,并向Sink模块输出
Sink模块
sink是处理数据的路由&分发,一份parse数据经过sink后可以分发为多份,每份的数据可以根据自己的过滤规则不同而有不同的数据 .但现在只做了:
- 数据二次过滤
- 向Store模块导入数据
Store模块
Store模块用于binlog事件的存储 ,目前开源的版本中仅实现了Memory内存模式MemoryEventStoreWithBuffer。其实现借鉴了Disruptor的RingBuffer。简而言之,你可以把其当做一个环形队列,如下:
- Put 操作:添加数据。event parser模块拉取到binlog后,并经过event sink模块过滤,最终就通过Put操作存储到了队列中。
- Get操作:获取数据。canal client连接到canal server后,最终获取到的binlog都是从这个队列中取得。
- Ack操作:确认消费成功。canal client获取到binlog事件消费后,需要进行Ack。你可以认为Ack操作实际上就是将消费成功的事件从队列中删除,如果一直不Ack的话,队列满了之后,Put操作就无法添加新的数据了。
Instance模块
每个instance都会模拟成一个mysql实例的slave.
- client向instance订阅服务,存到meta中
- parser启动,模拟slave接取对应的信息并导入sink
- sink将数据引入store
- client通过instance向store摘取数据
分库分表场景:比如产品数据拆分了4个库,位于不同的mysql实例上。正常情况下,我们需要配置四个CanalInstance。对应的,业务上要消费数据时,需要启动4个客户端,分别链接4个instance实例。
- 使用GroupEventParser,由GroupEventParser内部维护4个MysqlEventParser去4个不同的mysql实例去拉取binlog,
- 通过GroupEventSink并到一起到store中。
- client只需要启动1个客户端,链接这个CanalInstance即可.
Server模块
server包含多个instance实例,根据业务场景的不同大致可分为
右边的图是内置部署
直接在应用中嵌入CanalServerWithEmbeded,不需要独立部署canal。
左图表示的是Canal独立部署
不同的应用通过canal client与canal server进行通信,所有的canal client的请求统一由CanalServerWithNetty接受,之后CanalServerWithNetty会将客户端请求派给CanalServerWithEmbeded 进行真正的处理。
总结
我们可以利用CanalServerWithEmbeded进行扩展
- 与消息机制结合
- 与jdbc结合
- 其它