标题起的有些装B,好吧,我认了。
今天解决客户现场客户端卡死问题,午饭也木吃,在此做一个总结。
问题描述
客户向实施反馈,点击某个功能后,客户端就一直卡着了,直至挂掉。
相关介绍
(1)、C/S架构的ERP产品,现场产品版本较低。
(2)、最近有打业务模块的补丁和一个基础的补丁。
(3)、可重现。
问题分析
(1)、想到的第一步是缩小定位问题的点,具体来讲是客户端卡住了还是在服务端卡住了。
(2)、首先来判断是不是卡在服务端了,如果是卡在服务端的话,应该有较多线程处于阻塞或者等待状态,在此点该按钮时,相关线程也会阻塞住。此时就需要查看服务端线程状态了,比较方便的是,我们产品直接做了一个web项目的工具,通过访问特定的url就可以拿到服务端线程的堆栈,通过反复观察服务端线程,可以排除不是卡在服务端。
(3)、接下来则是分析客户端,通过jconsole工具进行分析现场客户端线程,可以清晰的知道此时客户端调到哪了,并且通过jconsole检测并有出现死锁,调用栈如下:
说明:最上面的省略号表示省略了部分代码,下面的省略号表示省略了重复的代码。
根据堆栈信息,首先简单理了一下程序执行了什么;其次思考本地开发环境能否重现,是在哪个点执行路径发生了改变。
显然在本地环境并没有重现,因此对比jconsole中的调用栈信息,在调试代码一个方法一个方法的确认,发现如下方法在正常的情况下只执行了一次,而现场调用栈信息表示却执行了很多次,感觉直。com.xxx.yyy.fm.common.client.DefaultTablePrecisionFormat.doAfterDataFill(DefaultTablePrecisionFormat.java:87)
最后仔细分析这个类的该方法代码可知,当该方法内对应一行代码调用一个方法返回的值大于0时就会进入闭环的循环调用,形成了死循环,从而最终客户端崩掉。
因此只要在开发环境运行期做如下设置也可重现问题。
只要在第一次调入该方法时,运行期将该返回值改成大于0就会重现现场的问题-客户端卡到最后崩掉。
总结与思考
(1)、有童鞋看完,估计会闲我笨,其实多一点想象力或经验的话,直接分析这个线程信息就可以锁定到是哪个类的哪个方法里面出了问题。因为这个调用栈可以理解为三部分,启动单元,重复调用单元,进行时单元,进行时单元是调用栈的最顶端,它是重复调用单元某个时点更具体在执行什么的描述,因此可以99.99%确定是进入了死循环,99.9999%确定是程序问题而不是数据量大导致的。在分析相邻两个重复调用单元之间过度的方法,即可第一时间找到最可疑的点,从而分析验证解决。
(2)、该问题可重现,直接连调现场客户端环境,根据调用栈信息跟说不定也会更快的找出问题。
(3)、形成闭环循环调用,造成死循环代码的场景?
以我们产品来讲,进行了好几个层次模型的抽象,在代码上的体现是继承体系深,因此基础实现的一些通用功能往往都会留口子让我们重写,注册监听或者啥的,而出问题代码要干的事情其实就是设置业务对象表格的样式,其中要设置的对象是A,A要设置默认格式,而默认格式的实现是该领域通用对象B实现的,B则是通过向基础对象C(基础模块)注册监听进行回调,在回调的方法里面又拿表格对象(基础模块)做了一些事,这就是一种造成较为隐蔽死循环的场景。
4104

被折叠的 条评论
为什么被折叠?



