记一次微服务启动慢的问题定位

2020-11-16  本文已影响0人  onmeiei

流水账记录一下查找问题的步骤,抛砖引玉,希望对大家有所帮助。

背景

有这么一个微服务,单中心启动需要10分钟,跨中心(数据库与微服务不在同一个中心)启动需要20分钟。甲方老爷们说了,降不到10分钟以内,不接收运维工作,运维工作就都压在项目组头上。
所以,临危受命优化一下。

常规步骤

第一,查看日志,发现大部分时间浪费到了Spring Bean创建和绑定阶段。
第二,查看CPU,使用率不高,只有单线程飙到了60%,偶尔100%,其余线程在打酱油。

所以,

使用arthas工具收集火焰图追踪一下
  1. attach进程
  2. profiler start
  3. 等待,漫长的等待
  4. 等到微服务启动完成后,执行profiler stop命令

把生成的火焰图拿下来,看一下,泛红的都是Spring反射,其他的看上去都正常的。

是因为bean太多导致的吗?

用jmap -histo看一下对象的个数,发现也还好啊。但是发现有32个AnnotationConfigApplicationContext和32个DefaultListableBeanFactory,这个地方是需要关注一下的。

打通网络,用JDK自带的jvisualvm远程监控一下。

这种情况下,需要

因为这个项目里面有很多是跨团队提供的【产品】,并没有提供源代码,所以,需要另辟蹊径来找到怎么调用的。

写javaagent,统计一下方法的调用情况。

使用javassist,对上述两个类(LaunchedURLClassLoader和AbstractAutowireCapableBeanFactory)动态插入点代码。因为是调试,所以就很low地用System.out.println把问题代码打印一下就得了。

上面还发现的AnnotationConfigApplicationContext和DefaultListableBeanFactory被重复创建32次的问题,再构造函数里面插入点代码,看一下是哪个地方创建的这两个类。

方法很简单,插入的两行代码如下,请忽略我的粗暴和不优雅。

System.out.println("==>Invoked===>" + beanName);
new Throwable().printStackTrace();

在输出日志中,因为日志太大,先分割,然后再统计

# 分割文件,每100万行一个
split -l 1000000 console.log
# 做去重和统计
ls x* | while read f
do
  grep ^"==>Invoked==>" $f  > filtered.$f
  sort filtered.$f | uniq > uniq.$f
done
wc -l filtered.*
wc -l uniq.*

通过折腾,发现共创建了6300多个Bean(真不算多),但是predicateBeanType被调用了1100万次,这就过分了 。

再通过StackTrace,发现了问题。

问题
至此,优化掉60%无用启动时间,接下来剩下的启动时间再优化优化。
上一篇 下一篇

猜你喜欢

热点阅读