问题描述:
探索环境中的yarn的ResourceManager的内存随着使用时间不断的变大,最终导致用户无法访问yarn的资源管理页面,并且整个集群的调度变得异常缓慢,最终导致隔一段时间就需要重启一下Yarn。
问题分析:
1.最先想到的是由于内存泄漏导致,因此到集群上取Dump文件,最开始使用的是命令(jmap
-dump:live,format=b,file=dump.hprof 22439)获取dump文件的,但是导致的一个结果就是把ResourceManager搞挂了,后来才知道是因为探索环境的yarn使用的是G1垃圾回收机制,对于G1垃圾回收机制,使用上面的方式可能会将进程搞挂掉,改成下面的命方式即可成功的获取dump文件:jmap-J-d64 -dump:live,format=b,file=yarn-22439.dump 22439 。
2.通过对dump文件的内存泄漏检测分析,发现
在类AsyncDispatcher中的一个属性(BlockingQueue eventQueue)的对象占用了大量的内存,因此怀疑是该对象发生了内存泄漏。
3. 通过类AsyncDispatcher的源码,发现其内部类GenericEventHandler会在处理消息的时候将上面怀疑是内存泄漏的对象eventQueue的size打印出来,因此决定查看线上yarn的日志,看此队列的长度
下图是列出的队列的长度,可明显可以看出该队列的尺寸达到了上千万,这肯定是有问题的。而正常的大集群的日志中队列的长度只有几千,很明显该对象是有问题的。
4.再通过内存分析工具发现,上述有异常的队列中存放的都是ContainerFinishedEvent和ContainerCreatedEvent对象,因此去查看该对象是在哪被创建和消费的。
5. 通过对源码进行分析定位到ContainerFinishedEvent事件是在类SystemMetricsPublisher中被消费掉的,该类是将ContainerFinishedEvent封装成TimelineEntity实体之后通过Http的post方式发送给yarn的TimelineServer进程。
6. 综上分析,目前的主要原因是由于消费者(也就是往TimelineServer发送Event事件数据)发送数据的能力不够,导致消息队列中缓存的大量的消息,随着时间的推移,消息数据不断增多,从而导致yarn内存不够用,从而需要重启。
问题的解决:
方案一、
通过分析SystemMetricsPublisher的源码发现,如下图中标记的的代码,可以控制是否向TimelineServer发送测量数据,标记的代码对应的参数的值为:
yarn.timeline-service.enabled 和 yarn.system-metrics-publisher.enabled
于是将yarn.system-metrics-publisher.enabled的值设置为false,也就是关闭了发送测量数据的功能,问题解决。
方案二、
通过分析SystemMetricsPublisher的源码发现,在创建分发器的时候,有个参数(yarn. system-metrics-publisher.dispatcher.pool-size ,默认值为10)可以控制发送的线程池的数量,通过增大该值,理论上应该也是可以解决该问题的,但没有具体测试,因为是线上环境。