Spring Boot Devtools 为 Spring Boot 应用程序提供通常在 IntelliJ 等 IDE 中可用的功能,例如,当某些类或资源发生更改时,您可以在其中使用 restart an application 或强制 live browser reload。这在应用程序的开发阶段非常有用。
它通常与 IDE 结合使用,这样当在类路径中检测到它并且未禁用时,它将由 Spring Boot 与应用程序的其余部分一起启动。
虽然您可以将其配置为监控更多资源,但它通常会在您的应用程序代码、类和资源中查找更改。
重要的是要说,AFAIK,Devtools 将以 exploded 的方式监控您自己的类和资源,我的意思是,如果您覆盖整个应用程序 jar,重启过程将不起作用,只有如果你覆盖了classes 目录中的一些资源。
可以使用 Maven 测试此功能。请考虑从Spring Initializr 下载一个简单的蓝图,例如使用 Spring Boot、Spring Boot Devtools 和 Spring Web - 以保持应用程序运行。从终端,在包含pom.xml 文件的目录中,运行您的应用程序,例如,借助pom.xml 中包含的spring-boot-maven-plugin 插件:
mvn spring-boot:run
该命令将下载项目依赖项,编译并运行您的应用程序。
现在,在您的源代码中执行任何修改,无论是在您的类中还是在您的资源中,并从另一个终端在同一目录中重新编译您的资源:
mvn compile
如果您查看第一个终端窗口,您会看到应用程序已重新启动以反映更改。
如果您使用 docker 进行应用程序部署,尝试重现此行为可能会很棘手。
一方面,我不知道这是否有意义,但您可以尝试创建一个基于 maven 的图像并在其中运行您的代码,如上所述。您的Dockerfile 可能与此类似:
FROM maven:3.5-jdk-8 as maven
WORKDIR /app
# Copy project pom
COPY ./pom.xml ./pom.xml
# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B
# Copy source files
COPY ./src ./src
# Run your application
RUN mvn springboot:run
通过此设置,您可以使用docker cp 将您的资源复制到/app/target 目录,这将触发应用程序重新启动。作为替代方案,请考虑在容器中挂载一个卷,而不是使用 docker cp。
好多了,并且考虑到覆盖您的应用程序 jar 可能不起作用的事实,您可以尝试复制您的类和库依赖项,并以爆炸的方式运行您的应用程序。考虑以下Dockerfile:
FROM maven:3.5-jdk-8 as maven
WORKDIR /app
# Copy your project pom
COPY ./pom.xml ./pom.xml
# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B
# Copy source files
COPY ./src ./src
# Compile application and library dependencies
# The dependencies will, by default, be copied to target/dependency
RUN mvn clean compile dependency:copy-dependencies -Dspring-boot.repackage.skip=true
# Final run image (based on https://stackoverflow.com/questions/53691781/how-to-cache-maven-dependencies-in-docker)
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2
# Change working directory
WORKDIR /app
# Copy classes from maven image
COPY --from=maven /app/target/classes ./classes
# Copy dependent libraries
COPY --from=maven /app/target/dependency ./lib
EXPOSE 8080
# Please, modify your main class name as appropriate
ENTRYPOINT ["java", "-cp", "/app/classes:/app/lib/*", "com.example.demo.DemoApplication"]
Dockerfile 中重要的一行是这样的:
mvn clean compile dependency:copy-dependencies -Dspring-boot.repackage.skip=true
它将指示 maven 编译您的资源并复制所需的库。虽然对于运行 spring-boot-maven-plugin repackage 目标的典型 Maven 阶段是多余的,但标志 spring-boot.repackage.skip=true 将指示此插件不要重新打包应用程序。
使用这个Dockerfile,构建你的图像(例如,让我们标记它devtools-demo):
docker build -t devtools-demo .
然后运行它:
docker run devtools-demo:latest
使用此设置,如果您现在更改类和/或资源,并在本地运行 mvn:
mvn compile
您应该能够使用以下docker cp 命令强制容器中的重启机制:
docker cp classes <container name>:/app/classes
请再次考虑在您的容器中安装一个卷,而不是使用docker cp。
我测试了设置,它工作正常。
要记住的重要一点是替换您的爆炸资源,而不是整个应用程序 jar。
作为另一种选择,您可以采用类似于 cmets 中指示的方法并在远程模式下运行您的 Devtools:
FROM maven:3.5-jdk-8 as maven
WORKDIR /app
# Copy project pom
COPY ./pom.xml ./pom.xml
# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B
# Copy source files
COPY ./src ./src
# Build jar
RUN mvn package && cp target/your-app-version.jar app.jar
# Final run image (based on https://stackoverflow.com/questions/53691781/how-to-cache-maven-dependencies-in-docker)
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2
# Change working directory
WORKDIR /app
# Copy artifact from the maven image
COPY --from=maven /app/app.jar ./app.jar
ENV JAVA_DOCKER_OPTS "-agentlib:jdwp=transport=dt_socket,server=y,address=*:8000,suspend=n"
ENV JAVA_OPTS "-Dspring.devtools.restart.enabled=true"
EXPOSE 8000
EXPOSE 8080
ENTRYPOINT ["/bin/bash", "-lc", "exec java $JAVA_DOCKER_OPTS $JAVA_OPTS -jar /app/app.jar"]
要让 Spring Boot Devtools 远程模式正常工作,您需要几件事(Opri 在他/她的回答中也指出了其中一些)。
首先,你需要在你的应用jar中配置spring-boot-maven-plugin到include the devtools(否则默认会被排除):
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
然后,您需要为配置属性spring.devtools.remote.secret 设置一个值。此属性与 Spring Boot Devtools 中远程调试的工作方式有关。
远程调试功能由两部分组成,客户端和服务器。基本上,客户端是您的服务器代码的副本,它使用spring.devtools.remote.secret 配置属性的值来针对服务器验证自己。
此客户端代码应为 run from an IDE,并且您将 IDE 调试过程附加到从该客户端公开的本地服务器。
在客户端监控的资源中执行的每个更改,请记住,与在您的服务器中执行的一样,都会被推送到远程服务器,并在必要时触发重新启动。
如您所见,从开发的角度来看,此功能再次更合适。
如果您需要通过覆盖您的 jar 应用程序文件来实际重新启动您的应用程序,也许更好的方法是将您的 docker 容器配置为在您的 ENTRYPOINT 或 CMD 中运行一个 shell 脚本。此 shell 脚本将监视某个目录中的 jar 副本。如果由于您的docker cp 而导致该资源发生更改,则此 shell 脚本将停止当前正在运行的应用程序版本 - 此应用程序应该从不同的位置运行以避免更新 jar 时出现问题 - 将当前 jar 替换为新的,然后启动新的应用程序版本。不一样,但请考虑阅读this related SO answer。
无论如何,当您在容器中运行应用程序时,您都在尝试为其提供一致且独立于平台的部署方式。从这个角度来看,与其监视 docker 容器中的更改,更方便的方法可能是生成并部署具有这些新更改的容器映像的新版本。使用 Jenkins、Travis 等工具可以极大地自动化此过程。这些工具允许您定义 CI/CD 管道,例如,响应代码提交,可以使用您的代码动态生成 docker 映像,并进行相应配置,稍后将此映像部署到某些 docker 风格的服务或Kubernetes,在本地或云端。其中一些,尤其是 Kubernetes,但 swarm 甚至 docker compose 也将允许您执行滚动更新,而不会或在最少的应用程序服务中断的情况下。
总之,它可能不符合您的需求,但请注意,您可以直接使用spring-boot-starter-actuator 或与Spring Boot Admin 一起使用,例如,重新启动您的应用程序。
最后,正如 Spring Boot Devtools 文档中已经指出的那样,您可以尝试不同的选项,不是基于重新启动,而是基于应用程序重新加载,在热插拔中。此功能由JRebel 等商业产品提供,尽管也有一些开源替代品,主要是dcevm 和HotswapAgent。 This related article 提供了有关这最后两个产品如何工作的一些见解。 This Github project 提供了有关如何在 docker 容器中运行它的补充信息。