Java 开发

线上 JVM 堆内存不足引发BeanCreationNotAllowedException

言七墨 · 3月29日 · 2020年 · 225次已读

背景

某个周六的中午,领导突然找我说线上有大量报错,让我看一下。我发现日志中出现了很多下面的报错信息,而且持续了一段时间(后面才发现收到了告警邮件,但是当时手机没有提醒,强行甩锅…):

org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'xxxController': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:208)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.web.method.HandlerMethod.createWithResolvedBean(HandlerMethod.java:323)
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:367)

排查过程

  • 查找是否有慢查询:当时找到了一个慢查询sql,但是分析后,应该不是影响本次报错的根本原因
  • 查找是否有其它报错日志:本服务基本上都是这个报错,其它调用方服务因为本服务的影响,出现了请求500的报错
  • 切换读源:由于此块业务是核心业务且还是双写+读阶段,故赶紧将读请求转移到原始项目
  • 观看 Grafana 监控:后来其中一个同事说docker的cpu使用率docker的内存使用率在某个时间节点开始发生了持续的断层(容器自动重启),仔细一看,在断层开始前,系统流量出现了一个小高潮。然后赶紧找运维同事帮忙检查下容器自身的配置。果不其然,本服务的几个实例配置的JVM堆内存不够用了,增大配置后,调用请求恢复了正常。

报错分析

系统出现峰值的时候,确实流量上去了一些,但是伴随出现了一些慢请求(上面提到的慢查询sql),当时的请求由于数据关联性比较强,要查询的数据比较多,创建的对象也就比较多,从而造成频繁的GC,导致各个请求的延时都比较严重。Springboot自带的测活接口(/alive)也不例外,出现了测活超时,从而导致容器频繁自动重启,在容器重启的时候,会发生下面的情况:

  1. BeanFactory工厂正在销毁
  2. 此时调用刚好执行到去工厂中获取bean(bean是单例)
  3. 造成上文中的报错

后续

  • 增量修复数据(通过增量迁移Job很快修复完成)
  • 切换读源:将读请求切到本服务
  • 针对上文提到的慢查询,优化代码查询逻辑
  • 运维层面:运维同事说后期会动态监控系统调用量,保证动态分配实例资源
0 条回应