Canal架构组件
转载自—https://blog.csdn.net/u013200380/article/details/80963569#31-canal的设计理念
1.mysql的binlog
它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间。主要用来备份和数据同步。
binlog 有三种模式:STATEMENT、ROW、MIXED
1.STATEMENT 记录的是执行的sql语句
2.ROW 记录的是真实的行数据记录
3.MIXED 记录的是1+2,优先按照1的模式记录
举例来说,下面的sql
1 | update user set age=20 |
对应STATEMENT模式只有一条记录,对应ROW模式则有可能有成千上万条记录(取决数据库中的记录数)。
1.2 mysql 的主从复制过程
- Slave 上面的IO线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
- Master 接收到来自 Slave 的 IO 线程的请求后,通过负责复制的 IO 线程根据请求信息读取指定日志指定位置之后的日志信息,返回给 Slave 端的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息在 Master 端的 Binary Log 文件的名称以及在 Binary Log 中的位置;
- Slave 的 IO 线程接收到信息后,将接收到的日志内容依次写入到 Slave 端的Relay Log文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master- info文件中,以便在下一次读取的时候能够清楚的高速Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”
- Slave 的 SQL 线程检测到 Relay Log 中新增加了内容后,会马上解析该 Log 文件中的内容成为在 Master 端真实执行时候的那些可执行的 Query 语句,并在自身执行这些 Query。这样,实际上就是在 Master 端和 Slave 端执行了同样的 Query,所以两端的数据是完全一样的。
当然这个过程本质上还是存在一定的延迟的。
mysql的binlog文件长这个样子。1
2
3
4mysql-bin.003831
mysql-bin.003840
mysql-bin.003849
mysql-bin.003858
1.3 canal能够同步数据的原理
理解了mysql的主从同步的机制再来看canal就比较清晰了,canal主要是听过伪装成mysql从server来向主server拉取数据。
canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
mysql master收到dump请求,开始推送binary log给slave(也就是canal)
canal解析binary log对象(原始为byte流)
2. quick start
直接引用 阿里的就不错,这里是一个阿里的单机版配置。
https://github.com/alibaba/canal/wiki/QuickStart
主要的点就是:
- 数据库配置,需要记录bin_log 需要使用row模式,需要创建用户给canal使用
- canal配置相应的主库地址,server_id在mysql的集群中不能重复,过滤哪些表使用的正则。
- 这个是一个单机的canal的搭建,并不是HA的模式。
3. canal 的设计
3.1 canal的设计理念
canal的组件化设计非常好,有点类似于tomcat的设计。使用组合设计,依赖倒置,面向接口的设计。
3.2 canal的组件有哪些
- canal server 这个代表了我们部署的一个canal 应用
- canal instance 这个代表了一个canal server中的多个 mysql instance ,从这一点说明一个canal server可以搜集多个库的数据,在canal中叫 destionation。
每个canal instance 有多个组件构成。在conf/spring/default-instance.xml中配置了这些组件。他其实是使用了spring的容器来进行这些组件管理的。
3.3 instance 包含的组件
这里是一个cannalInstance工作所包含的大组件。截取自 conf/spring/default-instance.xml
1 | <bean id="instance" class="com.alibaba.otter.canal.instance.spring.CanalInstanceWithSpring"> |
eventParser 最基本的组件,类似于mysql从库的dump线程,负责从master中获取bin_log
eventSink 数据的归集,使用设置的filter对bin log进行过滤,工作的过程如下。
eventStore 用来存储filter过滤后的数据,canal目前的数据只在这里存储,工作流程如下。
metaManager 用来存储一些原数据,比如消费到的游标,当前活动的server等信息
alarmHandler 报警,这个一般情况下就是错误日志,理论上应该是可以定制成邮件等形式,但是目前不支持
3.4 各个组件目前支持的类型
canal采用了spring bean container的方式来组装一个canal instance ,目的是为了能够更加灵活。
eventParser 目前只有三种
- 1.1. MysqlEventParser 用于解析mysql的日志
- 1.2. GroupEventParser 多个eventParser的集合,理论上是对应了分表的情况,可以通过这个合并到一起
- 1.3. RdsLocalBinlogEventParser 基于rds的binlog 的复制
eventSink 目前只有EntryEventSink 就是基于mysql的binlog数据对象的处理操作
eventStore 目前只有一种 MemoryEventStoreWithBuffer,内部使用了一个ringbuffer 也就是说canal解析的数据都是存在内存中的,并没有到zookeeper当中。
metaManager 这个比较多,其实根据元数据存放的位置可以分为三大类,memory,file,zookeeper
canal通过这些组件的选取可以达到不同使用场景的效果,比如单机的话,一般使用file来存储metadata就行了,HA的话一般使用zookeeper来存储metadata。
4. canal的工作过程
4.1 启动时去MySQL 进行dump操作的binlog 位置确定
工作的过程。在启动一个canal instance 的时候,首先启动一个eventParser 线程来进行数据的dump 当他去master拉取binlog的时候需要binlog的位置,这个位置的确定是按照如下的顺序来确定的(这个地方讲述的是HA模式哈)。
- 在启动的时候判断是否使用zookeeper,如果是zookeeper,看能否拿到 cursor (也就是binlog的信息),如果能够拿到,把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog
- 通过1拿不到的话(一般是zookeeper当中每一,比如第一次搭建的时候,或者因为某些原因zk中的数据被删除了),就去配置文件配置当中的去拿,把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog
- 通过2依然没有拿到的话,就去mysql 中执行一个sql show master status 这个语句会显示当前mysql binlog最后位置的信息,也就是刚写入的binlog所在的位置信息。把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog。
后面的eventParser的操作就会以内存中(MemoryLogPositionManager)存储的binlog位置去master进行dump操作了。
mysql的*show master status *操作
1 | mysql> |
4.2 数据在dump回来之后进行的归集(sink)和存储(store)
sink操作是可以支撑将多个eventParser的数据进行过滤filter
filter使用的是instance.properties中配置的filter,当然这个filter也可以由canal的client端在进行subscribe的时候进行设置。如果在client端进行了设置,那么服务端配置文件instance.properties的配置都会失效
sink 之后将过滤后的数据存储到eventStore当中去。
目前eventStore的实现只有一个MemoryEventStoreWithBuffer,也就是基于内存的ringbuffer,使用这个store有一个特点,这个ringbuffer是基于内存的,大小是有限制的(bufferSize = 16 * 1024 也就是16M),所以,当canal的客户端消费比较慢的时候,ringbuffer中存满了就会阻塞sink操作,那么正读取mysql binlog的eventParser线程也会受阻。
这种设计其实也是有道理的。 因为canal的操作是pull 模型,不是producer push的模型,所以他没必要存储太多数据,这样就可以避免了数据存储和持久化管理的一些问题。使数据管理的复杂度大大降低。
上面这些整个是canal的parser 线程的工作流程,主要对应的就是将数据从mysql搞下来,做一些基本的归集和过滤,然后存储到内存中。
4.3 binlog的消费者
canal从mysql订阅了binlog以后主要还是想要给消费者使用。那么binlog是在什么时候被消费呢。这就是另一条主线了。就像咱们做一个toC的系统,管理系统是必须的,用户使用的app或者web又是一套,eventParser 线程就像是管理系统,往里面录入基础数据。canal的client就像是app端一样,是这些数据的消费方。
binlog的主要消费者就是canal的client端。使用的协议是基于tcp的google.protobuf,当然tcp的模式是io多路复用,也就是nio。当我们的client发起请求之后,canal的server端就会从eventStore中将数据传输给客户端。根据客户端的ack机制,将binlog的元数据信息定期同步到zookeeper当中。
差不多主要的操作就是这些吧。
5. canal的目录结构(不包含lib目录)
配置父目录:
在下面可以看到,
1 | canal |
这里是全部展开的目录
1 | canal |
原文链接:https://blog.csdn.net/u013200380/article/details/80963569