把责任归咎于驴
「把责任归咎于驴」,这是最近读过的 『Java 性能优化实践』书里,提到的一个 性能优化的反模式。
也就是我们在不了解事情真相的时候,固执的认为一定是另一个原因导致的,驴莫名其妙就成了背锅的。
这在我们技术开发领域特别常见。
书中举例说他们当年在某个团队性能优化时,发现一次大规模的中断故障,导致一个数据库的某个表被锁定了,每次重启之后才会恢复。因为ORM 使用的是 Hibernate,所以人们对 Hibernate 都不太了解,但一致认为是 Hibernate 导致的。直到后来才发现是 某个代码段中的catch 块并没有清理数据库连接,开发人员花了一天时间才真相大白。
我写这篇是接上回关于 条件注解要说的一个问题(Spring 创建Bean 时是怎样判断条件的?)。
当时我开发一个组件,用于在运行时给指定的方法生成注解,并在类内新增方法,功能基本是通过 ByteBuddy 实现。但为了能使用配置中心的能力,免于修改配置重新打包,所以将组件的执行时机放在了 Spring Boot 应用启动时,在 远程配置中心加载后,但 Bean Class 加载前开始。
本地测试功能一切正常,然后预上线的时候忽然发现,新增方法与注解的日志都已经输出,但组件并没有启动开始工作。这就太奇怪了。
做为一个 Java 老司机,第一时间投入到了对于打包,以及 ByteBuddy 的生成是否生效的怀疑中。
但是,包是新打的, ByteBuddy看着也在正常工作。预上线环境不能 debug, 好在可以使用 Arthas。 于是用 Arthas 的 jad 命令,反编译了相关类的class,发现生成的源码里并没的运行时动态增强的内容。
然后就带着这个认识,开始各种分析 ByteBuddy 的 Transformer 为什么不能正常工作,有没有和 SpringBoot 有怪异的问题。看了好久一无所获。
一筹莫展的时候,开始了最原始的「对比-分析」办法。在线下能正常工作的环境中,分析 class, 发现 Arthas jad 的输出也是没有增强的内容。而通过 JDK 的 SA 来保存运行时的 class 再本地反编译时发现是包含增强的内容。
这就推理出 Arthas 的 jad 并不能输出增强后的内容,预上线的环境应该也成功作了增强,只是组件其它原因导致没有正常工作。
目光在从增强方面转移到其它地方的时候,才想起来组件工作的 Starter 中,为了根据配置不同能设置组件的启停,特意加了「@ConditionalOnProperty」,但是在预上线的环境中漏掉了相应的属性配置,所以对应的 Configuration 没有工作。
问题原因清楚了,修复也很简单。但因为一上来就把责任归咎于驴,还一通操作猛 如虎,结果回头发现自己二百五。好在排查过程中发现, SA 生成的源码和 Arthas 是有区别的,也算有所收获。 : -)
相关阅读
这里是「Tomcat那些事儿」,关注源码|实战|成长等话题,欢迎关注,一起成长。