确保 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的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot确保Web应用安全(登陆认证)

Spring Boot - 确保数据属于当前登录用户

如何确保 @ExceptionHandler(Exception.class) 在 Spring Boot 中最后被调用?

如何确保 Spring Boot 额外的 Jackson 模块具有相同的版本?

如何确保在 Apache Tomcat 中运行的 Spring Boot 应用程序中替换了环境变量占位符?

如果它是一个 Rest 控制器,如何确保 Spring boot 自动编组一个类的对象