记一次cloud netflix metrics导致生产环境频繁FULL GC问题

1.查看GC日志

grep 'Full' /data/logs/gc_log/gc.log

红框内 回收大小->回收后大小(总大小),耗费时间 secs。可以看到full gc 一分钟就执行了3次,STW每次接近20秒,已经严重影响业务。

也可以使用命令 jstat -gc 1535 2000 10 查看

jstat -gc 1535 2000 10意味每隔2000毫秒输出 jvm进程1535 的GC信息 打印10次。

2.导出jvm 堆文件分析

此时只能进行对堆文件进行分析哪些对象导致频繁Full GC

使用sz命令进行下载但是当时heapDump.hprof 文件大小是6G ,linux上sc命令最大支持4G故无法使用sz命令进行下载。

所以需要对文件进行切割。

命令:tar cvf heap.tar heapDump.hprof

生产环境出问题上线导致的问题先回滚,不确定的问题先重启。怎么恢复快怎么来搞,哪怕是临时性的。不然再紧张的情况下去修复可能会补丁上打补丁出新问题,引发连锁问题。

漫长的等待后再windows下使用copy 命令将heap.tar.aa,heap.tar.ab,heap.tar.ac,heap.tar.ad,合并为最终的heap.tar文件。

命令:copy /b heap.tar.aa+heap.tar.ab+heap.tar.ac+heap.tar.ad heap.tar

下载Memory Analyzer进行对dump文件分析。

Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

下载对应安装包,由于最新版本必须jdk11,我是jdk8所以我下载了比较老的版本。

运行MemoryAnalyzer.exe

ParseHeapDump.bat 是可以再linux系统中直接使用的,比较适合那些生产环境中对应dump后的文件十几个G或者几十个G不好下载的情况下去根据dump文件生成报表可以直接分析。网上有对应教程(jvm内存快照dump文件太大,怎么分析 - 神风炼 - 博客园

导入文件后分析完看到占用最多的是AutomicLong类,这个类代码中没用到,所以基本可以断定是其他依赖包里的,再继续往下看com.netflix.servo.monitor.MonitorConfig ,com.netflix.servo.monitor.BasicTimer。字面意思可以知道是来自netflix的监控什么东西的。

3.定位问题类

直接搜下ServoMonitorCache类是干嘛的。

4.定位问题类引用

类中只有一个map并且名字是timerCache 问题出现的地方应该就是这里,但是具体干什么的不知道。

spring boot 自动装配配置文件(spring.factories)中找到来自spring cloud 的netflix core包,

然后找到ServoMetricsAutoConfiguration (别问为什么因为只有他是metrics包下的)

ServoMetricsAutoConfiguration类中import了MetricsInterceptorConfiguration 看似拦截器的配置类

拦截器中声明了一个MetricsHandlerInterceptor 拦截器

MetricsInterceptorConfiguration 类中初始化的拦截器MetricsHandlerInterceptor 这个拦截器没看懂干什么的先跳过

@ConditionalOnBean spring自动装配就是各种conditionOn xxx 这里意思是如果容器中有RestTemplate 就会执行,并且吧ServoMonitorCache构造注入,ServoMonitorCache正是dump文件分析出来的hashMap对象过多的根源线程,进去详细了解。

MetricsClientHttpRequestInterceptor 显然是个过滤器的名字到底拦截什么的我也不知道,那就看注释,该类的头部注释中是这样写道的。

/**
 * Intercepts RestTemplate requests and records metrics about execution time and results.
 *
 * @author Jon Schneider
 */

大意是拦截RestTemplate 的request,record,time,result 。所以是不是我们凡是使用restTemplate的请求都会被拦截到?

在过滤器intercept方法内打了断点启动debug 然后发起restTemplate请求果然打到了断点上。

 

但是执行完以后不管是否异常或者超时都有一个肯定会执行的finally代码块,

然后servoMonitorCache 有一个getTimer方法 传入的是monitorConfigBuilder

monitorConfigBuilder来自MetricsTagProvider.clientHttpRequestTags 方法。进去看下干什么的。

DefaultMetricsTagProvider.clientHttpRequestTags 内部就算记录了http请求的method,uri,status,clientName 然后组装为一个不可以更改的map

然后构造为monitorConfigBuilder,此时可以进入servoMonitorCache的getTimer方法内查看做了什么,这里就跟分析dump文件结果中出现问题根源的类ServoMonitorCache对应上了,刚开始不知道为什么会调用到该类。

原来就是如果每次的monitorConfig对象不存在timerCache这个Map中的话,就put进去。

那如果是重复的url则不会有问题,如果不重复的话就会一直增加。而该项目再使用restTemplate的场景主要是基于feign调用其他业务的情况,而对于其他业务的调用为了防止篡改都使用了动态字符串签名加密,而签名加密的数据都是直接跟在真实请求地址后面的。例如baidu.com?time=2021101015&sign=123abc.这样上面图1中的uri永远都不可能重复,也就导致map中不可能有该数据,从而导致map越来越大。占用JVM内存区域越来越高,前期数据量比较小,后面过不了多久内存就无法满足GC就会一直再工作。

虽然可以看到图中3标有如果timerCache到达一定容量大小后(1000)log.warn的输出,但是该项目QPS一直比较大上线根本看不到。所以metrics认为系统中最多对接1000个外部接口,不可能再多了。。。。

再回过头来看这个metrics到底干什么的,

5.问题解决

spring boot依赖的组件都是通过自动装配的,各种@Condition @import @ConditionalOnProperty matchIfMissing 等,所以回到metrics的装配类中看下对于metrics的装配是否可以进行配置。

然后再配置文件中配置spring.cloud.netflix.metrics.enabled = false 即可。禁用metrics的请求监控,因为我们项目有自己的监控工具。

然后启动应用再次发起restTemplate的请求则不会执行先前打断点的拦截器了,也就不会再有重复uri添加到map中了。

TODO:后续应该考虑是否对于生产JVM的频繁full gc进行监控,例如某一时间段进行了频繁的full gc发送告警。

最后我是没整明白为什么netflix的这个metrics监控为什么会把url后面如果有拼接的参数就视为一个新的url.好歹你识别到url中有?切割下再识别也可以。而且是默认开启监控 - -!我吐了。

 关注我的公众号 LearnMoreDoMore 可以及时看到更多技术分享文章(不会该死的卖课广告)。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值