前言
- 本篇要实现的功能
1.1 cannal监听指定的表
1.2 切面写入操作信息,通过traceId进行关联
1.3 MQ消费消息,将diff日志记录到指定的traceId记录中
Cannal的过滤(只监听指定的表)
简单介绍Cannal启动流程
- 如果启动模式为spring(mode或者globalConfig.mode),则读取设置配置文件,调用SpringCanalInstanceGenerator#generate去生成实例
canal.instance.global.spring.xml = classpath:spring/file-instance.xml
过滤机制初始化
- 解析过滤处理(spring/file-instance.xml)
1.1 读取exmaple/instance.properties的canal.instance.filter.regex属性,没有则默认为.…
1.2 解析完后,调用AbstractMysqlEventParser#setEventFilter设置eventFilter属性
AviaterRegexFilter#filter过滤
- 在LogEventConvert#parseRowsEventForTableMeta进行过滤拦截
1.1 调式发现fullname为test.user(数据库.表名),所以可以修改正则表达式只监听满足指定表
TraceId的设计
- 通过TraceId关联操作信息和数据变更信息,但TraceId怎么传递了?
1.1 TraceId在切面入口生成,然后存到调用链上下文
1.2 在JDBC数据库操作时,更新业务表的TraceId字段
1.3 Cannal监听时取出变更后的TraceId字段,与操作信息进行绑定 - 为了解决跨线程池的问题,使用阿里开源的ThreadLocal组件:transmittable-thread-local
2.1 更多关于调用链上下文传递参考:http://college.creditease.cn/detail/221
TraceId传递
- 在切面开始处,通过日志上下文设置TraceID
- 在JDBC数据库操作时,取出TraceID
public class OperationLogContext { final static TransmittableThreadLocal<String> ttlContext = new TransmittableThreadLocal<>(); public static void setTraceId(String traceId){ ttlContext.set(traceId); } public static String getTraceId(){ return ttlContext.get(); } public static void clear(){ ttlContext.remove(); } }
删除操作的特殊处理
- 因为删除操作,不会将traceId更新到业务表中,所以需要特殊处理
- 因为是通过traceId进行关联,所以在删除之前先做一次更新traceId字段,监听时只改traceId时不记录变更
TraceID的生成
- traceId要保证唯一性,每次操作都拥有唯一的TraceId
- 参考sky-walking的traceId生成
2.1 TraceIdGenerator生成规则:2位version号 + 1位时间戳(毫秒数) + 1位进程随机号(UUID后7位) + 1位进程数号 + 1位线程号 + 1位线程内序号
总结
- 写到这里时,基本要实现的效果已经出来了
- 后续会对一些细节进行完善
2.1 在引入TTL时,尽量避免修改代码,考虑通过JavaAgent方式
2.2 diff日志里面包含的是字段名称,将字段名称转移成可读的中文名称