最近在用 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 优雅停机方法
这个问题的解决研究了一天, 如果是我哪里配置不对导致的, 欢迎指点.