6

我有一个使用 mvn 构建的 Spring Boot 2.25 应用程序。根据此文档,我添加

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

从文档中:

由于 DevTools 监控类路径资源,触发重启的唯一方法是更新类路径。更新类路径的方式取决于您使用的 IDE。在 Eclipse 中,保存修改后的文件会导致类路径更新并触发重新启动。在 IntelliJ IDEA 中,构建项目(Build -> Build Project)具有相同的效果。

随着应用程序的运行,我尝试了一个简单的

touch /path/to/app.jar

期望应用程序重新启动,但什么也没发生。

好吧,也许它正在做一些更聪明的事情。我修改了一些源 .java,重新编译了 .jar,并cp用它来替换正在运行的 .jar 文件,然后……什么也没发生。

也来自文档

DevTools 依赖应用程序上下文的关闭挂钩在重新启动期间将其关闭。如果您禁用了关闭挂钩 (SpringApplication.setRegisterShutdownHook(false)),它将无法正常工作。

我不这样做。

DevTools 需要自定义 ApplicationContext 使用的 ResourceLoader。如果您的应用程序已经提供了一个,它将被包装。不支持直接覆盖 ApplicationContext 上的 getResource 方法。

我不这样做。

我在 Docker 容器中运行它,如果这很重要的话。从文档中:

运行完全打包的应用程序时,开发人员工具会自动禁用。如果您的应用程序是从 java -jar 启动的,或者它是从一个特殊的类加载器启动的,那么它被认为是“生产应用程序”。如果这不适用于您(即如果您从容器运行应用程序),请考虑排除 devtools 或设置 -Dspring.devtools.restart.enabled=false 系统属性。

我不明白这意味着什么或是否相关。

我想重新编译一个 .jar 并将其替换在正在运行的 docker 容器中,并在不重新启动容器的情况下触发和应用程序重新启动。我怎样才能做到这一点?

编辑:我mvn用来重建 jar,然后docker cp在正在运行的容器中替换它。(IntelliJ IDEA 声称要重建项目,但实际上并没有触及 jar 文件,但这是另一回事。)我正在寻找一个非 IDE 特定的解决方案。

4

4 回答 4

3

Spring Boot Devtools 为 Spring Boot 应用程序提供了通常在 IntelliJ 等 IDE 中可用的功能,例如,当某些类或资源发生更改时,您可以在其中重新启动应用程序或强制重新加载实时浏览器。这在应用程序的开发阶段非常有用。

它通常与 IDE 结合使用,这样当在类路径中检测到它并且没有被禁用时,它将由 Spring Boot 与应用程序的其余部分一起启动。

尽管您可以将其配置为监视更多资源,但它通常会在您的应用程序代码、类和资源中查找更改。

重要的是,AFAIK,Devtools 将以爆炸的方式监控您自己的类和资源,我的意思是,如果您覆盖整个应用程序 jar,重启过程将不起作用,只有当您覆盖classes目录中的一些资源时。

可以使用 Maven 测试此功能。请考虑从Spring Initializr下载一个简单的蓝图,例如 Spring Boot、Spring Boot Devtools 和 Spring Web - 以保持应用程序运行。从终端,在包含该pom.xml文件的目录中,运行您的应用程序,例如,借助以下spring-boot-maven-plugin插件中包含的插件pom.xml

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 编译您的资源并复制所需的库。尽管对于目标运行的典型 Maven 阶段是多余的spring-boot-maven-plugin repackage,但该标志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。

作为另一种选择,您可以采用类似于评论中指示的方法并在远程模式下运行您的 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 在他/她的回答中也指出了其中一些)。

首先,您需要将devtoolsspring-boot-maven-plugin配置为包含在您的应用程序 jar 中(否则默认情况下将被排除):

<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配置属性的值对服务器进行身份验证。

此客户端代码应该从 IDE 运行,并且您将 IDE 调试过程附加到从该客户端公开的本地服务器。

请记住,在客户端监控的资源中执行的每个更改都与在您的服务器中一样,被推送到远程服务器,并且在必要时会触发重新启动。

如您所见,从开发的角度来看,此功能再次更合适。

如果您需要通过覆盖您的 jar 应用程序文件来实际重新启动您的应用程序,也许更好的方法是配置您的 docker 容器以在您的ENTRYPOINTCMD. 此 shell 脚本将监视某个目录中的 jar 副本。如果该资源发生更改,由于您的docker cp,此 shell 脚本将停止当前正在运行的应用程序版本 - 此应用程序应该从不同的位置运行以避免更新 jar 时出现问题 - 用新的 jar 替换当前 jar,然后启动新的应用程序版本。不一样,但请考虑阅读这个相关的 SO 答案

在任何情况下,当您在容器中运行应用程序时,您都在尝试为其提供一致且独立于平台的部署方式。从这个角度来看,与其监视 docker 容器中的更改,更方便的方法可能是生成并部署具有这些新更改的容器映像的新版本。使用 Jenkins、Travis 等工具可以极大地自动化此过程。这些工具允许您定义 CI/CD 管道,例如,响应代码提交,可以使用您的代码动态生成一个 docker 映像,并进行相应配置,稍后将该映像部署到某些 docker 风格的服务或Kubernetes,在本地或云端。其中一些,尤其是 Kubernetes,但聚集了一个均匀的docker compose同样,将允许您执行滚动更新,而不会或最小的应用程序服务中断。

总而言之,它可能不符合您的需求,但请注意,您可以spring-boot-starter-actuator直接使用或与Spring Boot Admin一起使用,例如,重新启动您的应用程序。

最后,正如 Spring Boot Devtools 文档中已经指出的那样,您可以尝试不同的选项,不是基于重新启动,而是基于应用程序重新加载,在热交换中。此功能由JRebel等商业产品提供,尽管也有一些开源替代品,主要是dcevmHotswapAgent这篇相关文章提供了有关这最后两种产品如何工作的一些见解。这个 Github 项目提供了有关如何在 docker 容器中运行它的补充信息。

于 2021-04-29T22:44:49.160 回答
1

我在使用 intellij idea 时遇到了类似的问题,我在某处看到您必须使用构建按钮才能使其工作。

在此处输入图像描述

在 jsp 中,应用程序重新加载文件,它不是完全自动的,因为 intellij 会自动保存 -> 这种行为是默认的,但我认为有一种方法可以改变它。-> 手动录制,然后自动重新加载。

仅适用于 jsp 应用程序,如果您使用标准应用程序尝试此操作,它将创建双帧执行 (swing)

于 2021-04-27T07:22:28.693 回答
1

我不支持,因为如果您尝试过这些事情,您并没有明确表示,但是:

  • 尝试将其设置为 true:(SpringApplication.setRegisterShutdownHook(true))

  • 尝试在 dockerfile 中手动添加此属性 -Dspring.devtools.restart.enabled=true

我知道它说默认情况下应该为真,但请尝试手动执行

也许向我们展示 dockerfile。

后来编辑:在文档中看到了这个:

默认情况下,重新打包的档案不包含 devtools。如果您想使用某些远程开发工具功能,您需要禁用 excludeDevtools 构建属性以包含它。Maven 和 Gradle 插件都支持该属性。

Spring Boot 开发者工具不仅限于本地开发。您还可以在远程运行应用程序时使用多种功能。远程支持是可选的,要启用它,您需要确保devtools包含在重新打包的存档中:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludeDevtools>false</excludeDevtools>
            </configuration>
        </plugin>
    </plugins>
</build>

然后你需要设置一个spring.devtools.remote.secret属性,例如:

spring.devtools.remote.secret=mysecret

远程开发工具支持分两部分提供;有一个接受连接的服务器端端点,以及一个您在 IDE 中运行的客户端应用程序。设置 spring.devtools.remote.secret 属性时,服务器组件会自动启用。客户端组件必须手动启动。

春天的文件

于 2021-04-27T07:30:31.297 回答
0

为了使用 devtools 重新启动应用程序,您需要确保以下事项。

  1. 使用任何 IDE 或构建工具(如 maven gradle)来启动应用程序
  2. 使用 java -jar devtools 将不起作用,因为它会打包应用程序。
  3. 使用 maven,您可以运行像 mvn spring-boot:run 这样的应用程序

有关详细信息,请参阅官方文档。

于 2021-05-01T14:03:53.653 回答