一个小BUG的解决过程。
2014-07-21 17:50
253 查看
我们的系统中有一个组件是接收从服务器推送过来的用户在线状态的通知消息并通知给自己的客户端UCService。这个组件以JAR包形式提供给UCService。此为背景。
一天测试人员报告说总是突然发生所有的通知消息都收不到了。以下是追查过程:
1 检查日志,发现我们的组件确实收到了服务器推送过来的消息。
2 没发现组件向UCService推送消息的日志。当然代码里有推送相关的日志。
3 检查组件这部分的代码。代码收到服务器的消息,会把消息放到一个队列里,由另一个线程负责从队列里取消息并发送给UCService。那可能的错误点有两个:或者是写队列的代码有BUG,或者取队列的代码有BUG。
4 根据现象描述,出故障之后,所以的新消息都无法推送,那明显是某个线程退出了。既然一直都有收到消息的日志,那一定是从队列里取消息并发送的线程不存在了。
5 检查这部分的代码。代码如下:
1 public void run() {
2 while (true) {
3 try {
4 OnPresenceChangedSubInfo info = queue.take();
5 if (info != null) {
6 PresenceInfo presenceInfo = info.getPresenceInfo();
7 List<String> subscriberUserList = info
8 .getSubscriberUserList();
9 listener.onPresenceChanged(presenceInfo, subscriberUserList);
10 }
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 }
15 }
肯定是循环体里的代码抛出没有被捕捉的异常了。那为什么没被捕获呢?问题找到了。这里只捕获了InterruptedException,而对其它可能发生的异常并没有捕获。而第9行里调用了一个由UCService实现的回调方法,这个方法很有可能抛出RuntimeException或者其它的Throwable。到底可能是哪种错误需要捕捉呢?需要好好分析它的代码... ...
6 我的答案是:不需要!这个线程不需要处理UCService的错误,我们要做的只是容错,也就是捕获异常并记录它。把第11-12行代码改为:
11 } catch (Throwable e) {
12 logger.writeError(e);
7 修改代码并重新部署后,故障消失。
BUG点评:
这是一个典型的对异常处理不当的BUG。我们的目标是保证线程绝不会异常退出,那只捕获非RuntimeException只能保证你的代码编译通过,而不能保证它能正常运行。
而有BUG的代码尤其错误的是,它在捕获异常后仅仅是把它打印在标准错误输出上,而这非常容易造成错误信息丢失。这种处理方式纯粹是把HelloWorld里的编程水平带到工作中来了。
关于JAVA的异常处理,可以写成好几本书。对于本文中提到的场景,我们只关心线程是否能正常运行,对异常的处理要求并不高,不需要按照异常的各类进行个性化的处理。所以直接捕获Throwable就好。
一天测试人员报告说总是突然发生所有的通知消息都收不到了。以下是追查过程:
1 检查日志,发现我们的组件确实收到了服务器推送过来的消息。
2 没发现组件向UCService推送消息的日志。当然代码里有推送相关的日志。
3 检查组件这部分的代码。代码收到服务器的消息,会把消息放到一个队列里,由另一个线程负责从队列里取消息并发送给UCService。那可能的错误点有两个:或者是写队列的代码有BUG,或者取队列的代码有BUG。
4 根据现象描述,出故障之后,所以的新消息都无法推送,那明显是某个线程退出了。既然一直都有收到消息的日志,那一定是从队列里取消息并发送的线程不存在了。
5 检查这部分的代码。代码如下:
1 public void run() {
2 while (true) {
3 try {
4 OnPresenceChangedSubInfo info = queue.take();
5 if (info != null) {
6 PresenceInfo presenceInfo = info.getPresenceInfo();
7 List<String> subscriberUserList = info
8 .getSubscriberUserList();
9 listener.onPresenceChanged(presenceInfo, subscriberUserList);
10 }
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 }
15 }
肯定是循环体里的代码抛出没有被捕捉的异常了。那为什么没被捕获呢?问题找到了。这里只捕获了InterruptedException,而对其它可能发生的异常并没有捕获。而第9行里调用了一个由UCService实现的回调方法,这个方法很有可能抛出RuntimeException或者其它的Throwable。到底可能是哪种错误需要捕捉呢?需要好好分析它的代码... ...
6 我的答案是:不需要!这个线程不需要处理UCService的错误,我们要做的只是容错,也就是捕获异常并记录它。把第11-12行代码改为:
11 } catch (Throwable e) {
12 logger.writeError(e);
7 修改代码并重新部署后,故障消失。
BUG点评:
这是一个典型的对异常处理不当的BUG。我们的目标是保证线程绝不会异常退出,那只捕获非RuntimeException只能保证你的代码编译通过,而不能保证它能正常运行。
而有BUG的代码尤其错误的是,它在捕获异常后仅仅是把它打印在标准错误输出上,而这非常容易造成错误信息丢失。这种处理方式纯粹是把HelloWorld里的编程水平带到工作中来了。
关于JAVA的异常处理,可以写成好几本书。对于本文中提到的场景,我们只关心线程是否能正常运行,对异常的处理要求并不高,不需要按照异常的各类进行个性化的处理。所以直接捕获Throwable就好。
相关文章推荐
- 一个新手接触手游项目碰到的bug及解决过程汇总
- 项目中一个Bug的解决过程
- 解决工作中遇到的一个"打开,保存"文件框的bug的过程
- 一个新手接触手游项目碰到的bug及解决过程汇总2
- 一个疑难bug的解决过程
- 一个bug的解决过程
- 一直以来我(每个从事linux开发的人)深受“bug”的困扰,好像“bug”不足以描述这种被问题困扰的无奈。因为当在驱动或BSP的开发过程中,所碰到的问题比解决一个bug难得多。 Li
- android一个下拉放大库bug的解决过程及思考
- 记录一个前端bug的解决过程
- ionic3内容置顶按钮,在csdn一个demo基础上修改的,解决按钮在过渡过程中忽隐忽现的bug...
- 一个关于 ie 浏览器的 bug 解决过程和思考
- 一个长时间parse的bug解决过程
- 记一个疑难bug的解决过程
- 用递归算法解决VC中CEdit的一个Bug
- Visual C++6.0一个小BUG的解决方法
- SQL Server 2005 Reporting Services的一个bug及其解决方法
- Visual C++6.0一个小BUG的解决方法
- 安装NET过程中反复提示有一个程序需要重启,但是重启依旧解决办法
- 探讨C#.NET下DropDownList的一个有趣的bug及其解决办法
- [ASP.NET 2.0]PageParser.GetCompiledPageInstance中的一个Bug及解决方法