确保 spring boot 和 liquibase 接收和处理 SIGTERM
Posted
技术标签:
【中文标题】确保 spring boot 和 liquibase 接收和处理 SIGTERM【英文标题】:Ensuring spring boot and liquibase receive and handle SIGTERM 【发布时间】:2019-09-21 02:26:47 【问题描述】:目前在容器化环境 (ECS) 中运行 SpringBoot 应用程序,我观察到容器在启动期间终止且仍持有 Liquibase 变更锁的情况。
这会导致之后旋转的所有容器出现问题,最终需要人工干预。
是否可以确保如果进程收到 SIGTERM,它会优雅地处理终止并释放锁?
我已经通过 InitProcessEnabled(在 CloudFormation 模板中)启用并使用“exec java ...”作为我们使用的 java 代理来确保容器正在接收信号,并在这种情况下正常关闭。
【问题讨论】:
您找到解决问题的方法了吗?我们的应用在 Wildfly 中运行时遇到了同样的问题。 现在在 Liqubase 存储库中创建问题:github.com/liquibase/liquibase/issues/1311 不,很遗憾我没有找到解决这个问题的方法,我们禁用了 liquibase 并采取了另一种方法。就我个人而言,我仍然很想知道它是如何进行的(我想我应该在他们的回购中提出一个问题)。 @bayetovsky 看看这里***.com/a/65564995/1704634。使用 liquibase-sessionlock 扩展,一旦容器终止,它将自动释放锁。 【参考方案1】:嘿嘿,
正如 GitHub 问题中所述,我有一个解决方法。尚未实施解决方案。
您可以在运行 spring boot 之前手动注册一个关闭钩子。该钩子应该确保终止被推迟到 liquibase 完成。
package dang;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
@SpringBootApplication
public class DangApplication
public static void main(String[] args) throws InterruptedException
Thread thread = new GracefulShutdownHook();
Runtime.getRuntime().addShutdownHook(thread);
new SpringApplicationBuilder(DangApplication.class)
.registerShutdownHook(true)
.logStartupInfo(true)
.build()
.run();
Runtime.getRuntime().removeShutdownHook(thread);
还有钩子:
package dang;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class GracefulShutdownHook extends Thread
@SneakyThrows
@Override
public void run()
super.run();
log.info("Shutdown Signal received.. Searching for Liquibase instances!");
boolean liquibaseIsRunning = true;
while (liquibaseIsRunning)
Map<Thread,StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
for(Map.Entry<Thread, StackTraceElement[]> entry : stackTraces.entrySet())
StackTraceElement[] stackTraceElements = entry.getValue();
for (StackTraceElement stackTraceElement : stackTraceElements)
if (stackTraceElement.getClassName().contains("liquibase") && stackTraceElement.getMethodName().contains("update"))
try
log.warn("Liquibase is currently updating");
entry.getKey().join();
liquibaseIsRunning = false;
catch (InterruptedException e)
log.error("Shutdown Hook was interrupted.. Fatal databaselock may be imminent", e);
if (Thread.interrupted())
throw e;
编辑
在实施我的解决方法后,liquibase 的贡献者共享了一个不同的解决方案(实际上只是通过 Spring 功能实现了相同的解决方案),这比我所做的要好得多:
package dang;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
@SpringBootApplication
public class DangApplication
public static void main(String[] args) throws InterruptedException
new SpringApplicationBuilder(DangApplication.class)
.initializers(ConfigurableApplicationContext::registerShutdownHook) // Registers application hook before liquibase executes.
.logStartupInfo(true)
.build()
.run();
【讨论】:
感谢分享这些信息!接受这个作为答案,因为它确实是最有帮助的(并且最接近解决问题)。还需要注意的是,@blagerweij 所做的指向***.com/a/65564995/1704634 的评论也是一个很好的答案和可能的解决方案。希望这可以帮助遇到类似问题的任何人 Heyo @bayetovsky 感谢您接受我的回答。在 liquibase github 页面上分享了我的解决方法后,一位贡献者建议使用 SpringBoot 关闭挂钩做同样的事情。看看我的编辑,你会看到一个巨大的差异(不需要 GracefulShutdown 类:D)以上是关于确保 spring boot 和 liquibase 接收和处理 SIGTERM的主要内容,如果未能解决你的问题,请参考以下文章
如何确保 @ExceptionHandler(Exception.class) 在 Spring Boot 中最后被调用?
如何确保 Spring Boot 额外的 Jackson 模块具有相同的版本?