确保 spring boot 和 liquibase 接收并处理 SIGTERM

Ensuring spring boot and liquibase receive and handle SIGTERM

目前 运行 容器化环境 (ECS) 中的 SpringBoot 应用程序,我观察到容器在启动期间终止的场景,同时它仍然持有 Liquibase 变更锁。

这会导致随后旋转的所有容器出现问题,最终需要手动干预。

是否可以确保如果进程收到 SIGTERM,它会优雅地处理终止并释放锁?

我已经确保容器正在通过 InitProcessEnabled(在 CloudFormation 模板中)启用并使用 "exec java ..." 作为我们使用的 java 代理来接收信号,并在此正常关闭情况。

嘿嘿,

如 GitHub 问题中所述,我有一个解决方法。解决方案尚未实施。

您可以在 运行 spring 启动之前手动注册一个关闭钩子。该钩子应该确保终止被推迟到 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();
  }
}