解决:[HikariPool-1 housekeeper] but has failed to stop it. This is very likely to create a memory leak

原创
2022/09/30 16:18
阅读数 111

最近在用 SpringBoot 2.7.3 写 demo 的时候发现, 如果请求查询数据库之后, 停止服务时就会报一个警告, 好像说是 HikariPool 资源没有释放

[HikariPool-1 housekeeper] but has failed to stop it. This is very likely to create a memory leak

其实看日志发现, HikariPool 资源最后是释放了的, 只不过是稍微晚了一点

网上关于这个问题的解决方案很少, 难道只有我遇到了么. 是我哪里配置的不好, 导致的吗?  我写的 demo 是最简单的 SpringBoot + mybatis + mysql 
经过各种查资料各种研究, 要解决这个问题, 个人觉得应该在内置 Tomcat 优雅停机之后才马上关闭数据源, 参考了内置 tomcat 的 GracefulShutdown 的代码

package com.scsoft.demo3.config;

import org.apache.catalina.Container;
import org.apache.catalina.Engine;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import com.zaxxer.hikari.HikariDataSource;

@Component
public class ShutdownHook implements ApplicationListener<ContextClosedEvent> {

	@Autowired
	private ServletWebServerApplicationContext servletWebServerApplicationContext;

	@Override
	public void onApplicationEvent(ContextClosedEvent event) {
		Engine engine = ((TomcatWebServer) servletWebServerApplicationContext.getWebServer()).getTomcat().getEngine();
		for (Container host : engine.findChildren()) {
			for (Container context : host.findChildren()) {
				while (isActive(context)) {
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						throw new RuntimeException(e);
					}
				}
			}
		}
//		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
//		System.out.println("ContextClosedEvent 调用" + sdf.format(new Date()));
		ApplicationContext applicationContext = event.getApplicationContext();
		HikariDataSource hikariDataSource = applicationContext.getBean(HikariDataSource.class);
		hikariDataSource.close();
	}

	private boolean isActive(Container context) {
		try {
			if (((StandardContext) context).getInProgressAsyncCount() > 0) {
				return true;
			}
			for (Container wrapper : context.findChildren()) {
				if (((StandardWrapper) wrapper).getCountAllocated() > 0) {
					return true;
				}
			}
			return false;
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}
}

以上代码是参考 org.springframework.boot.web.embedded.tomcat.GracefulShutdown 类中的 doShutdown 优雅停机方法

这个问题的解决研究了一天, 如果是我哪里配置不对导致的, 欢迎指点.

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部