写在前面
项目中因为涉及到其他系统的数据状态同步,但是部署在不同机房和区域,所以使用了阿里开源的canal进行数据状态的同步
问题分析
在维护这个项目的时候发现,系统在测试环境启动canal监听服务后,停止使用stop或kill命令都无法使服务停止,最后只能使用kill -9来强制杀掉进程
排查步骤
排查前是不知道是因为canal无法停止引起的。所以使用如下步骤来排查
1、jps -l确定进程确实一直存在,并获取到进程id
2、使用 jstack 查看当前进程id下有哪些异常的线程
拉到当前进程的线程信息首先看有没有系统内自写的代码所造成的的异常,这种可能性是最大的
结果真的发现有自写的代码导致线程一直在WAITING,如下图所示
也就是这里等待释放锁也就是AQS的state来控制,
继续看canal源码,这么写是canal为了保证只能有一个客户端监听,可以看到这里使用zk来进行节点创建,成功则获得锁,失败则进行监听zk,zk删除节点通知其他canal客户端来进行抢占创建节点,成功则获得锁,以此重复。
知道了canal的锁的获取和释放,我们继续看问题,进一步跟踪系统代码。发现系统里写了一个ShutdownHooks,用来控制canal的关闭,然后问题根源就出在这里了:
可以看到这里为了让canal线程可以正常走完业务流程,从而使用thread.join的方法,但是如果我们多节点启动canal的时候,会造成其他节点canal一直处于WAITING状态,从而导致线程无法关闭,进而整个服务无法正常关闭。
解决方法
所以需要进行改造,为canal客户端线程增加如下方法解除锁等待
然后再改造ShutdownHooks即可,这样服务就可以正常关闭了~