最近有版本特性要上线,但是在上线的过程中遇到了“阻碍”,使得上线过程不是很顺利,想必你也曾经遇到过吧!
1
问题背景
说说大概的场景吧。 由于系统里面增加了权限的限制,不同用户拥有不同的数据权限。 当前的方案是查询用户uid和对应的数据列表存放在本地内存里,并且需要定时捞取对应的关系数据存储在本地缓存LocalCacheMap中,key为uid,value为List。
接着其它接口则根据LocalCacheMap获取对应的数据权限进行相关的判断。
我们准备上线时,发现功能不正常,本来应该是能正常过滤权限数据的时候却没有生效?
但是其它同学在测试环境验证基本没问题的! 所以就有很大的疑问了。
我都不相信这功能真的在测试环境上OK? 对测试结果表示怀疑,所以我跟组内同学在测试环境验证一波,确实是没问题的。 那么继续查看检验代码逻辑,查看是否有哪里不严谨,可能存在隐藏的bug。
在看代码的过程中也发现了日志打的太少了,重点的地方都不打下,起码还能知道从哪里跟踪,(对于代码注释等规范,可见原创《 互联网Code Review最佳实践分享 》),所以对于这点暂且不说,我也不想说重新补打日志,然后重新打包,发版排查。
为了一探究竟,此时必然还是需要使用强大的工具Arthas,之前就已经解决过我的生产问题。
可见于原创: 《 实战使用Arthas排查生产问题: 实例方法接口调用 》,所以继续使用阿尔萨斯Arthas来排查定位。
2
排查解决问题
1)排查一 :
此时借助Arthas,尝试调用获取缓存数据:
调用实例方法,获取到的结果为null,说明缓存中没有我要的数据,这就很奇怪了。 然后我去验证拉去权限数据的接口,手动去调http接口,此时在服务器上使用curl手动调用接口,但是接口返回的443,所以我怀疑是不是调用的接口问题造成缓存数据为空? 此时找了运维同学帮忙确认下是不是网络的问题,最后排查结果确实是网络没有放行,于是等待网络问题解决之后继续上线流程。
但是,网络问题虽然解决了,再次验证功能还是跟刚才一样,why? 继续排查...
2)排查二 :
此时,再次借助Arthas,调用了refresh方法,来手动触发缓存刷新操作。
执行刷新操作,返回null,这是正常的,因为refresh返回void,此次刷新耗时在1132ms。
接着,再调前面获取缓存数据的接口:
数据出来了! ! ! 说明权限数据接口是正常的,拉取数据是正常,接着我们就在功能上进行验证,确实都正常了。
那么问题就出在刷新方法的调用上,是否没触发或者调用者已经没调用等等情况。 查看了代码之后才发现发现是使用TimerTask来定时执行任务,定时更新缓存数据。
Timer timer = new Timer(false); timer.schedule(new RefreshTask(), 10*1000L, 30*1000L); private class RefreshTask extends TimerTask { @Override public void run() { reflesh(); } } reflesh() { // 拉去权限数据 // 更新缓存数据 }
就是这样来维护缓存数据。 那么为什么它没执行? 按道理启动之后都会每30秒执行一次才对,但是为什么没有呢?
3)罪魁祸首 :
想必很多人知道TimerTask会存在一个问题,就是定时调度执行的方法如果没有捕获处理异常的话,那么它就会终止,基本上不会再运行了。 所以应该是这个问题造成的。
那么,应该要找到它抛异常的地方才能验证我们这个问题。 所以从日志里面找,最终发现:
确实是在启动不久(10秒左右)的地方,抛了NPE异常,所以这也验证了我们的问题,罪魁祸首就是它了。
4)解决:
ScheduledExecutorService executorService =
new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern( "schedule-task-%d").build());; executorService.scheduleWithFixedDelay( new RefreshTask() , 10*1000L, 30*1000L, TimeUnit.MILLISECONDS);
使用ScheduledThreadPoolExecutor来定时调度刷新缓存。 比TimerTask的好处就是出现异常也会继续重新定时调度。
3
总结
这种问题,虽说不是特别难的问题,但经验不是很丰富的开发人员却在日常中常会犯的,也会影响正常特性上线,造成发版阻碍,影响功能上线。
针对此次“事件”, 总结 一下,以免下次再犯:
1)日志,日志,要打印,要打印好。
2)尽量别用TimerTask,别踩坑,如要用一定要捕获处理好异常,一般建议使用ScheduledExecutorService代替。 (阿里规约)
3)要学会使用Arthas,在紧急“救火”中非常有用!
此“事故”真实发生,若有雷同,实属巧合。 :)
以上所述就是小编给大家介绍的《实战生产问题:真的别再使用 TimerTask 了》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Python 实战 Kafka 生产者 API
- 实战使用 Arthas 排查生产问题:实例方法接口调用
- 实战生产问题:真的别再使用 TimerTask 了
- 生产实战:如何正确地读取jar包中的File
- istio服务网格生产环境ingress网关部署SSL实战
- Elasticsearch生产环境索引管理深入剖析-搜索系统线上实战
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。