Spring Boot 2.1和Java 11中的Bean生命周期

4

我的项目从使用Java 8和Spring Boot 2.0.4迁移到Java 11和Spring Boot 2.1.0。在使用Java 8和Spring Boot 2.0.4构建应用并在Docker / Docker Compose中运行时,会调用@PostConstruct注释的方法,但是在迁移到Java 11和Spring Boot 2.1.0后,不再调用@PreDestroy注释的方法。

我已经尝试按照这里的说明,从注释切换到实现InitializingBeanDisposableBean,但���不会调用DisposableBean.destroy方法。

我还尝试添加依赖项javax.annotation-api版本1.3.2,但结果是相同的。

如何复现:

创建一个最小的Spring应用程序,并具有生命周期bean:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class Life implements InitializingBean, DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("--- Life.shutdown");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("--- Life.startup");
    }
}

从目标子文件夹启动Spring应用程序:
cd target
java -jar demo-0.0.1-SNAPSHOT.jar

当应用程序使用Ctrl+C停止时,会调用DisposableBean.destroy方法。
返回到父文件夹:
cd ..

使用Maven启动Spring应用程序:
mvn spring-boot:run

当应用程序使用 Ctrl+C 停止时,会调用 DisposableBean.destroy。
Dockerfile:
FROM openjdk:11.0.1-jre-slim
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT java -jar /app.jar

构建、运行和停止Docker镜像:

docker build -t demo .
docker run -p 8080:8080 demo
docker ps
docker stats 3ca5b804ab13
docker stop 3ca5b804ab13
docker logs 3ca5b804ab13

当应用程序使用docker stop停止时,不会调用DisposableBean.destroy。
docker-compose.yml:
demo:
  image: demo
  ports:
  - '8080:8080'

使用Docker Compose运行Docker镜像(模拟OpenShift):
docker-compose up
docker-compose down
demo_demo_1 exited with code 137

当使用docker-compose down停止应用程序时,DisposableBean.destroy不会被调用。

我怀疑Docker在发出SIGKILL之前正在尝试SIGTERM,因为10秒的延迟后容器才被杀死。


1
可能是某些东西正在使用 SIGKILL 杀死 JVM 吗? - Karol Dowbecki
没有看到任何配置文件、Docker Compose 文件等,很难诊断任何问题。你能否在直接运行而不涉及 Docker 的情况下重现该问题? - Gimby
2个回答

1
我认为我找到了解决方案(在这篇博客文章中):在Dockerfile中使用exec表单而不是shell表单,这样Docker发出的SIGTERM会影响java进程而不是bash进程(后者不会将信号转发给任何子进程)。
  • Shell表单(使用/bin/sh -c shell执行): ENTRYPOINT java -jar /app.jar
  • Exec表单(无需shell执行): ENTRYPOINT ["java", "-jar", "/app.jar"]

1

有很多地方可能会出错。首先建议确定是Java/Spring部分存在问题还是Docker/环境相关的问题。从问题描述来看,似乎是与Java相关,但实际上我怀疑不是Java/Spring的问题。

所以,mvn spring-boot:run可以正常工作,并且我看到您使用Spring Boot插件将Spring Boot应用程序打包为jar(app.jar)。这也是潜在问题的地方,因为Spring Boot使用特殊的类加载器在运行时加载内容。

因此,为了完全排除“Java/Spring”部分,请转到target目录并运行java -jar app.jar(当然,确保本地安装了Java 11)。如果无法正常工作,则调查Java/Spring部分;否则,请继续进行Docker部分。

应用程序有望按预期工作。

现在,关于Docker设置。

在运行Docker Compose并发现失败后,您可以使用以下命令:

docker ps -a // -a flag to see container ids of containers that were stopped for whatever reason as well.

现在获取已退出的Java进程的ID并检查其日志:
docker logs <ID_OF_THE_EXTED_CONTAINER_GOES_HERE> 

现在的情况是应用程序上下文启动失败(可能与网络相关或其他问题有关,在没有实际日志的情况下很难确定),因此出现了问题。
另一个可能的问题是应用程序“太重”(我的意思是它超出了Docker容器强制执行的某些配额)。
您可以运行docker stats <CONTAINER_ID>命令以实时查看其内存/ CPU使用情况,或从应用程序内部收集度量标准。

谢谢您的建议 - 我已经编辑了我的问题,包括额外的步骤。因此,当直接在JVM中运行应用程序(从命令行或通过Maven)时,它可以按预期工作,但在Docker中运行时却不能。我的演示应用程序是一个非常轻量级的Spring项目,具有简单的“hello”方法,可响应localhost:8080上的HTTP请求。docker日志命令显示与控制台窗口相同,因此我没有找到任何额外的信息。 - Tore I. Christoffersen
使用docker stop命令停止容器后,从docker logs命令得到的输出将以容器启动时显示的最后一行结尾:2018-12-03 05:40:12.487 INFO 6 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 3.791 seconds (JVM running for 4.374) - Tore I. Christoffersen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接