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

2024-04-25 14:56:43 浏览数 (2)

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

代码语言:javascript复制
[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 的代码

代码语言:javascript复制
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 人点赞