canal 停机后继续从上次位置拉取binlog的原理

讨论的前提是 CanalMetaManager 和 CanalLogPositionManager 都使用 file、zookeeper或者mix(memory+zookeeper)类型的实现类来保存。


两个概念:meta和logPosition

meta:记录ack()的位置

logPosition:记录拉取binlog的位置


当调用CanalServer.get()时,开始位置会先取meta(但是并不作为起始位置,没理解meta有何用处)

CanalServer.ack()时,再更新meta的位置


1.多个url来源的处理

监控数据binlog是canal的任务。CanalInstance创建多个CanalEventParser来监控多个url来源的数据。

  • 一个进程有一个CanalServer;
  • 一个CanalServer有一个或多个CanalInstance;
  • 一个CanalInstance有一个或多个CanalEventParser(当CanalInstance的getGroupSize()>1时,有多个CanalEventParser,即一个CanalInstance监控多个url的binlog),一个CanalEventStore(以RingBuffer的形式存储从binlog转换而来的对象),一个CanalMetaManager。
  • 一个CanalEventParser有一个CanalLogPositionManager。

2.事务的处理

由于CanalInstance可能需要处理多个url来源的数据,但是一个CanalInstance只有一个CanalEventStore,这意味着多个url来源的数据需要放到同一个对象存储里面,但是这些数据的最小单位并不是一个事务,所以需要使用EventTransactionBuffer将一个完整的事务放到CanalEventStore中,避免多个url来源的数据在一个事务中被拆散:

  • AbstractEventParser.parseThread 线程中获取到数据后,会调用EventTransactionBuffer.add(entry)利用CanalEventSink将一个完整的事务放到CanalEventStore中。

3.停机后,从上次停机的位置执行的处理

实现过程

CanalEventParser有自己的CanalLogPositionManager来保存位置,且某些实现可以持久化到文件或者zookeeper。当停机重启后,可以获取上次位置继续执行:

  • 每个CanalEventParser从自己对应的url拉取binlog时,开始位置会先取CanalLogPositionManager中的LogPosition,如果没有才会利用show master status从mysql中取binlog的当前位置。当获取数据并将放入CanalEventStore后,会更新CanalLogPositionManager的logPosition。
数据重复问题
  • 由于file、zookeeper、mix类型的CanalLogPositionManager可以持久或存储上次的位置到文件中,所以下次启动项目后可以从原来的位置继续运行。但是logPosition的位置是先放入内存,再有定时器定时持久化到文件或者zookeeper中的,所以是不是会有一些延迟,从而导致下次开始时会有一些已处理过的重复数据?
数据丢失问题
  • CanalEventStore使用RingBuffer的形式进行存储,操作数据有put()/get()/ack()三个方法。上面说到“当获取数据并将放入CanalEventStore后,会更新CanalLogPositionManager的logPosition”,这意味着CanalLogPositionManager中记录的是put()的位置。而put()到CanalEventStore中后,这些数据可能还没有处理就down机了。这些数据没有处理,而下次开始后不会再次获取这些数据?
canal原代码中group类型的CanalInstance从上次停机位置继续执行有问题(已修改)

当canalinstance为group类型时会有问题,问题原因是同一个canalInstance的多个CanalLogPositionManager持久化到文件或zookeeper时使用了相同的存储路径。

  • 以file类型的CanalLogPositionManager实现FileMixedLogPositionManager为例:Canal代码中将多个CanalLogPositionManager的位置都放在类同一个destination下的同名文件中,即:/{logPositionRoot}/{destionation}/parse.dat。导致CanalInstance为group时,虽然CanalInstance的多个CanalParser的存储位置的对象CanalLogPositionManager是分开的,但是多个对象却使用了同一个存储路径造成数据覆盖,从而使CanalInstance最终只有一个CanalEventParser能继续从上次位置开始执行。
特别注意:在切换过程中(即双方都正在提供线上服务时),不能停机。如果出现停机,则直接将任务废弃掉重新开始迁移数据,千万不要从上次位置继续执行,否则会出现数据覆盖。



-------------



目前canal-1.0.26-SNAPSHOT 对n:1的支持有问题。

n:1即:一个canalInstance中有多个数据来源,如多个不同地址的mariadb(10.1.200.144:3306/10.1.200.145:3306)

1.GroupEventSink 中的TimelineTransactionBarrier 中两个方法不匹配  isPermit()/clear()  

需要将   clear()中 if (isTransactionEnd(event)) 改为 if (isTransactionEnd(event) && inTransaction.get())

2.使用group时,CanalMetaManager  和 CanalLogPositionManager都只会保存一个canalInstance实例中的一个地址,但是group类型的canalInstance有多个地址

如何解决?


CanalMetaManager 和 CanalLogPositionManager

1)canalInstance->CanalMetaManager 1对1
2)canalInstance->CanalLogPositionManager 1对多 当groupDbAddresses的groupSize()>1时 为1对多
因为:
canalInstance->CanalEventParser 1对多
CanalEventParser->CanalLogPositionManager 1对1


CanalLogPositionManager
1.persistLogPosition() 从 binlog拉取数据,并放入CanalEventStore后 set位置
1)
AbstractEventParser.parseThread.run()
EventTransactionBuffer.add()
EventTransactionBuffer.TransactionFlushCallback.flush()
logPositionManager.persistLogPosition(AbstractEventParser.this.destination, position);
2.getLatestIndexBy() 从 binlog拉取数据前 get位置
2)
AbstractEventParser.parseThread.run()
MysqlEventParser.findStartPosition()
MysqlEventParser.findStartPositionInternal()
logPositionManager.getLatestIndexBy(destination)




CanalMetaManager
1.getCursor()
1)CanalServer.subscribe()
2)CanalServer.get()
3)AbstractCanalStoreScavenge()// getCursor()之后,会 cleanUtil(),即ack()  貌似没被调用过
2.updateCursor()
1)CanalServer.ack()
2)CanalServer.subscribe()

当调用CanalServer.get()时,开始位置会先取meta(但是并不作为起始位置,没理解meta有何用处)

CanalServer.ack()时,再更新meta的位置

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值