[spring-projects/spring-boot]launch.script 不应妨碍 sigterm 传播

2024-04-17 632 views
0

https://github.com/spring-projects/spring-boot/blob/069e7f3881bd95a75cae8ddd7abef143ab116296/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/启动/加载程序/工具/launch.script#L265

因为 run() 函数省略了“exec”的使用,所以一旦 java 被调用,它就会留下一个无用的 (??) shell 脚本进程在运行。

按照目前的情况,当这个 shell 父进程收到 SIGTERM 时,它不会将其传递给 Java,并且 Java 进程会继续存在。在某些情况下,这会干扰流程管理。

是否可以修改 run 函数以使用“exec”shell 命令来省去父进程? (参见问题 5273)

如果我们无法修改某些现有的运行,那么我们是否可以有替代方案来执行 exec 或提供修改运行行为的选项,甚至提供自定义启动脚本的选项?

谢谢

另请参阅https://github.com/spring-projects/spring-boot/issues/5273

回答

8

exec正如你所看到的,我们已经考虑并拒绝了#5273中使用的建议。在没有证据表明我们的推理有错误的情况下,我认为我们不会改变主意。您想尝试制作一个吗?

启动脚本已经可以通过配置构建来使用您选择的文件来自定义。详细信息请参阅文档。

0

前面的票证中的论点是关于改变 run() 的行为,这就是为什么我建议有可能采取替代行动,让我们称之为 mode=exec ,它在幕后使用 exec 。这种方法不会改变现有的行为,这是之前提出的反对意见。

--

虽然我可以用我自己的版本完全覆盖脚本,但在所有构建中执行此操作会增加复杂性,包括在这些构建中管理相同脚本的额外工作。

最好在脚本中添加一个新功能,恕我直言,这样每个人都可以以低成本受益。

8

#5273 中的反对意见还在于,为什么有必要这样做并不明显:

为什么需要使用sh spring-boot-app-exec.jar runas命令呢?您可以使 jar 本身可执行并按spring-boot-app-exec.jar默认run操作使用。

由于没有相反的信息,我假设您处于相同的情况,因此同样的问题适用。

最好在脚本中添加一个新功能,恕我直言,这样每个人都可以以低成本受益。

我不同意成本低。我们添加的每个配置选项都必须记录下来,并且用户在决定哪些功能满足他们的需求时必须了解这些选项。对于像这样已经存在解决方案的罕见问题(使用满足您需求的自定义脚本或以不同方式执行 jar),很难证明添加另一种方法来完成相同的事情是合理的。

0

我直接使用 chmod 的 jar 。

我没有做... sh spring-boot-app-exec.jar

问题在于嵌入式脚本本身是一个被调用的 shell,它会挂起并阻止信号传播。

默认的嵌入脚本显然不能很好地处理信号。我觉得记录这个附加功能会很容易。

5

我直接使用 chmod 的 jar 。

谢谢。当引用 #5273 时,如果您指出虽然您的情况相似,但您正在做的事情有一个关键的区别,那么会节省相当多的时间。

我觉得记录这个附加功能会很容易。

编写文档可能很难,也可能不难,但这不是问题。问题是配置选项带来了额外的复杂性。我们必须描述您何时可能需要run 的变体、何时可能需要当前行为、using在非交互式 shell 中的exec效果等。exec

正如我之前所说,据我们所知,对于一个没有影响很多人并且有其他解决方案的问题来说,很难证明添加和记录这种复杂性是合理的。除了能够使用自定义脚本添加 的使用之外exec,您还可以选择终止子 java 进程或使用 终止相关进程组kill -- -<parentPid>,其中parentPid是脚本 shell 的 pid。

我将标记此问题以引起团队注意,看看团队其他成员的想法。

1

仅当存在单独的进程组并且父进程能够与进程组进行互操作时,进程组方法才有效。

例如,如果父进程是 java,则 Process.destroyForcously() 或 Process.destroy() 不理解进程组,因此只能杀死 bash 包装器,使 java 进程成为孤立进程。

3

听起来您的情况比您描述的要多。这是您第一次提到,显然,您正在使用 Java 通过启动脚本来 shell 和执行另一个进程。

为了避免浪费更多时间,您能否详细描述一下您正在尝试做什么以及您面临的问题,并暂时忽略任何可能的解决方案。

1

老实说,我觉得这不可思议。为什么我们有一个进程调度程序是 JVM 的用例很重要?该限制仍然存在,并影响任何无法使用进程组的人(无论出于何种原因)。

7

@Johnlon 了解有关该问题的更多背景信息确实有助于我们找到我们愿意支持的最佳长期解决方案。我同意 @wilkinsona 的观点,我们不想仅为这一用例添加额外的配置选项。我们已经提供了自定义嵌入式启动脚本的功能,因此已经可以嵌入使用 exec 的替代方案(如果这是您真正想要的)。

我想知道使用是否exec只是该问题的一种可能的解决方案。也许使用trap可能是另一种选择,它允许我们将信号传播到进程而不需要使用exec.

我想进一步调查一下,但如果我们有关于您的具体情况的更多详细信息,这肯定会有所帮助,这样我们就不会实施对您没有帮助的修复。

0

鉴于已排除向现有函数添加 exec 的可能性,那么下一个最简单的方法是使用一个单独的函数来执行 exec。任何试图用 trap 正确处理信号的事情都是困难且不完整的。最初要求的最佳方法是通过消除无用的 shell 进程来修复信号传播。

6

您能描述一下您的意思吗?在单独的函数中使用如何防止https://github.com/spring-projects/spring-boot/issues/15229#issuecomment-190132375exec中讨论的问题?

任何试图用 trap 正确处理信号的事情都是困难且不完整的。

我还没有研究过它,所以我不知道它会有多困难或不完整。您过去是否尝试过做类似的事情但没有成功?

最初要求的最佳方法是通过消除无用的 shell 进程来修复信号传播。

恐怕我仍然不同意这一定是对所有用户来说最好的方法。

我认为如果您详细描述您正在尝试做什么以及什么对您不起作用,这个问题可能会更有成效。您在之前的评论中说过:

例如,如果父进程是 java,则 Process.destroyForcously() 或 Process.destroy() 不理解进程组,因此仅杀死 bash 包装器,使 java 进程成为孤立进程

这听起来像是您正在尝试从另一个 java 进程启动可执行 JAR,然后稍后停止它。是这样吗?如果是这样,您能分享一段代码来准确地向我们展示您在做什么吗?这将使我们能够专注于实际问题,并尝试找到希望对每个人都有效的侵入性最小的解决方案。

8
Process p = Runtime.getRuntime().exec("springbootexec.jar");
p.destroyForcibly();

这不会杀死 java 子进程,因为 springboot shell 脚本会妨碍终止信号。

Kill 无法被捕获,因此 shell 脚本中的陷阱无法解决此问题。

唯一的解决方案是中间的 Spring Boot shell 脚本不存在,而是让 java 进程直接接收 Kill 信号,这只有在脚本使用 exec 时才有可能。

6

我认为大多数人可能只是java -jar ..直接使用,因为现在运行午餐脚本在容器化环境中有点无用,在这种情况下不传播信号是一个非常非常大的问题。 exec 云原生应用程序的最佳选择,您不需要 pesudo-init 系统,因为该脚本是您的 pid 1。更多信息请参见:https: //docs.docker.com/develop/develop-images/dockerfile_best-practices/ #入口点

目前它确实可以是可选的,但我想说未来看起来不像现在的方法!

编辑:正如这条评论https://github.com/spring-projects/spring-boot/issues/5273#issuecomment-190132375一样,它可能已经过时了,而且,由于这些问题缺乏吸引力,我认为人们只是不要费心使用启动脚本,了解受此影响的人群有多大,以及那些只是继续前进而没有挖掘我们正在发生的设计问题的事实的人群会很有趣!

6

无用是一种相当强烈的说法,但我们不希望很多人(如果有的话)在部署到容器化环境时使用启动脚本。请注意,据我们了解,信号传播问题特定于单独的 Java 进程正在编排作为完全可执行 jar 启动的应用程序的环境。绝大多数使用启动脚本的人不会受到这个问题的影响,因此这对我们来说是一个低优先级的问题。对于那些有问题的人,可以使用满足他们需求的自定义启动脚本。

3

这是一个很大的。应该修复。

2

我认为这个问题至少应该记录下来,毕竟这是使用 Java API 调用 Java 程序并得到意想不到的结果。