1. 技术选型:kettle + kettlepack
kettle:数据同步工具,提供丰富的组件,通过托拉拽的方式,设计一套数据抽取流程
kettlepack:负责支持远程carte服务,调度kettle的任务(转换/作业)
2. kettle 数据同步案例
① 设计start_job_i_tap_bond_info 作业,作业中添加sync_i_tap_bond_info 转换封装了具体的业务实现
② batch_insert 是第一次全量同步业务表数据,使用的是表输出组件,batch_update 在完成第一次同步之后增量更新,使用的是插入/更新组件
3. 运行环境
① kettle机器配置:单台2核2G
② 调度kettle任务:选用的是kettle-pack,通过调用远程的carte服务,底层其实就是发送Http请求执行kettle的作业
kettle-pack类似于开源的xxl-job,可以配置定时策略,定时重复执行kettle的任务
4. 配置kettlepack,开启任务调度
在kettle-pack的作业管理菜单,新增一个定时任务,选泽kettle文件,配置定时策略比如每分钟执行一次。开启后,就会每分钟执行一次
5. kettle 内存溢出问题
Exception in thread "sync_i_tap_bond_info - update_next_time" java.lang.OutOfMemoryError: GC overhead limit exceeded
大概意思就是说,JVM花费了98%的时间进行垃圾回收,而只得到2%可用的内存,频繁的进行内存回收(最起码已经进行了5次连续的垃圾回收),JVM就会曝出java.lang.OutOfMemoryError: GC overhead limit exceeded错误
6. 问题如何一步步定位解决的
Unable to load transformation [batch_insert_i_tap_bond_info] : can't find directory xxx。kettle-pack使用的是v0.7.3版本,这个问题已经提到社区,说是后续修复更新。但是只要你的业务只要稍微复杂点,由于kettle的转换是并发执行的,如果需要控制一些步骤的顺序,就不能规避装换里面包含转换。但是经测试,使用远程carte服务调度作业没有这个问题,所以目前可以先用作业。
① 那么内存溢出的问题到底是为什么?
通过JDK自带的工具jvisualvm(或者其它)查看运行时内存的使用情况,当时发现老年代内存使用达到98%,并且手动触发GC,老年代也几乎没有回收什么东西。当时猜测kettle 调度作业会产生一个常驻内存,同时查阅网上相关资料,猜测kettle运行本身也会占用一定的内存。
② 是不是2G内存不够,换成4G测试
经过以上分析猜测,尝试将机器配置由2核2G换成2核4G,并且加大测试压力,配置2个任务每分钟执行一次,分别同步2张100万的数据表。监控JVM内存运行情况,发现老年代在不断增长,猜测可能是动态年龄机制导致直接挪到老年代,于是手动触发Full GC,发现老年代没有回收。最终经测试,发现4G也会撑爆。
③ 难道kettle真的有内存泄漏的重大问题?
一方面看看Github 上有没有人提出这个问题,另一方面在想既然是开源项目,不应该存在这种严重的低级问题。
④ 最终如何找到问题原因的?
停掉任务,观察老年代有没有释放资源,手动触发GC发现老年代内存还是几乎没有回收,但是随着时间过去,老年代释放了一些。并且通过查看日志,发现间隔性的打印如下日志内容:Cleaned up job xxx with id d837be4b-b690-47fe-a8d4-b330fe445aa0,看意思就知道在清除某个对象。于是根据" Cleaned up job "到kettle源码中定位到具体代码如下:
这段程序意思就是,后台定时任务每隔20秒扫描过期的对象,将它清除
过期的定义:任务完成或者停止,并且当前时间和任务的日志时间之差超过objectTimeout
objectTimeout:kettle 默认 1440 单位分钟,即一天
为什么就配了一个任务,会产生大量的常驻内存。通过抓包工具(推荐Wireshark),抓取到在kettlepack触发任务执行时的http请求如下:
根据 /kettle/executeJob 定位到kettle源码中具体的那块代码,如下:
可以看出,每次任务执行一次,就会往缓存中添加一个新的Job对象
⑤ 最终解决方式:objectTimeout=1440 默认配的是一天,即将执行完成的Job对象保留一天时间,可以在kettle.properties中自定义该属性的值,这边改的是5分钟,即任务执行完成后5分钟就会被扫描成过期的对象,让它尽量在年轻代就被回收掉。