最近用Netty写IM参考了 FreddyChen 大佬的开源,关于这个不断重连的问题,也请教了大佬,大佬前后都十分热心,共同找分析共同解决,谢谢大佬!
开源一个自用的Android IM库,基于Netty+TCP+Protobuf实现
背景:
期间发现一个非常重要的问题,就是ChannelInboundHandlerAdapter的channelInactive、exceptionCaught方法在重新登录,或者网络状态有变的情况下,可能会出现无限触发的问题.
问题场景:
channelInactive、exceptionCaught中,我们采用了自动重连,但会发现一个问题,在重连成功的情况下,还会重连,不断的反复重连.
问题原因:
1 : 主动退出
如果在用户主动退出的情况下,比如,用户主动退出聊天或者主动杀死App,这种情况下,是不需要重连的
2 : 业务逻辑异常
Channel中所有未捕获的异常,都会触发exceptionCaught方法,比如后台返回的数据格式不对,产生了没有捕获的异常,就会触发exceptionCaught,所以,在ChannelInboundHandlerAdapter的各种方法中,最好不要有没有处理的业务逻辑异常.
当天解决了这两个问题后,不断重连Bug不再重现,以为问题解决了,开开心心的回家,结果第二天来了,不断重连的问题又出现了,但这次出现的原因是,我退出登录后重新登录,才会出现.
分析:
首先,这次出现不断的重连原因,只有在第二次登陆后,才会重连.即便是第二次登录,但IM所有相关的连接和组件,都是重新初始化的, 所以我先把重连方法closeChannelAndReConnect(ctx);注释掉.注释后第二次登录,发现重连问题不再重现.所以,基本可以可以肯定,一定是重连的方法里,某个异常或者动作,导致了当前的连接主动关闭了
验证:
在channelInactive()方法打了断点,通过堆栈信息我看到触发channelInactive()方法的条件:wasActive&&isActive()
然后我继续查看isActive()方法发现:isActive = ch.isOpen()&&ch.isConnected()
看到这儿我就明白为什么会出现不断重连的问题了:
由于我在重连的之前,是先把所有的IM连接停止掉,包括线程池,具体方法看图:
但是,我没有清除Channel中的各种Handler,由于Handler在子线程中不停的轮训:
而这个task.run()回调的方法就是上面图2的方法.当我第一次登陆成功,Channel中就有了各种handler,而我在第二次登录的时候,由于第一次退出只是Channel.close()了,没有清除Channel中的各种handler,导致handler在轮训的时候,发现Channel是关闭的,所以就不断的触发不断的重连.....进入了死循环!
结论
关闭Channel之前,先清除掉Channel中的各种handler,如图
碎碎念:
1: 处理好各个handler中的透传逻辑,否则也是噩梦!
2: 各个重写方法中,确认是否需要调用父类的重写方法,比如是否需要调用super.channelInactive(),本质同上条
3: 如果有用,请点个喜欢