2

编辑:事实证明,由于错误的健康检查失败,我的 docker 容器正在关闭。这导致 kubernetes 探测失败太多次并杀死了 pod。由于命令来自 JVM 外部,因此代码可能无法知道 JVM 周围的容器何时被杀死。留下这个问题,以防有人找到答案。

我有一个 Java SE 项目,它读取处理文件的参数。我正在使用 Weld-SE 进行注射。运行几分钟后,焊接容器在没有解释的情况下关闭。以下是我的代码的骨架。在我的主要课程中

@ApplicationScoped
public class Main {

  @Setter //Lombok annotation
  private Scheduler scheduler;

  public void schedule(String[] filenames) throws InterruptedException {
    scheduler.schedule(filenames);
  }

  @PreDestroy
  public void cleanUp() {
    log.info("PreDestroy - Main");
    //clean up omitted
  }

  public static void main(String[] args) {
    Weld weld = new Weld();
    Main main = null;
    try (WeldContainer container = weld.initialize()) {
      main = container.select(Main.class).get();
      Scheduler scheduler = CDI.current().select(Scheduler.class).get();
      main.setScheduler(scheduler);
      main.schedule(args);
      log.info("Main - completed processing");
    } catch (Exception e) {
      log.info("Exception occurred: " + e.getMessage(), e);
    } finally {
      log.info("Main Finally - Shutting down Weld");
      CDI.current().destroy(main);
      weld.shutdown();
    }
  }
}

这是我的调度程序类

@Named
public class Scheduler {

  private ThreadPoolExecutor executor;
  private Map<Future<?>, Importer> beanMap = new HashMap<>();
  private static final Duration sleepDuration = Duration.ofSeconds(45);

  @PostConstructor
  public void init() {
    //setup executor
  }

  @PreDestroy
  public void cleanUp() {
    log.info("PreDestroy - Scheduler");
    //clean up omitted
  }

  public void schedule(String[] filenames) throws InterruptedException {
    for(String filename : filenames) {
      Importer caller = CDI.current().select(Importer.class).get();
      caller.setFilename(filename);
      Future<?> future = executor.submit(caller);
      beanMap.put(future, caller);
    }

    while(!isDone()) {
       Thread.sleep(sleepDuration.toMillis());
       log.info("Remaining threads: {}, getRemaining());
       destroyCompleted();
    }
  }

  //Returns true only when all threads are completed
  private boolean isDone() { //code omitted }

  //Returns number of not completed threads
  private int getRemaining() { //code omitted }

  //Clean up any completed Importers
  private boolean destroyCompleted() { //code omitted }
}

这是我的进口商代码

@Named
public class Importer implements Callable<String> {

  @Setter //lombok annotation
  private String filename;

  public void cleanUp() {
    log.info("PreDestroy - Importer");
    //clean up omitted
  }

  public String call() {
     List<String> content = //extract file content
     for(int i = 0; i < content.size; i++) {
       if (i+1 % 10000 == 0) {
         log.info("Processed {} rows in file {}", i+1, filename);
       }
       //rest of loop omitted
     }
  }
}

文件 test.txt 大约有 100,000 行,因此我可以判断崩溃发生在文件处理过程中。因此,在我的代码运行几分钟后,我在日志末尾得到以下信息

Processed 50000 rows in file test.txt
Processed 60000 rows in file text.txt
PreDestroy - Scheduler
PreDestroy - Importer
PreDestroy - Main
WELD-ENV-002001: Weld SE container 03128098-039d-46db-97e5-8538a52a38cc shut down

我在调试级别记录“org.jboss”。我可以看到容器在处理文件的过程中被关闭,但我不明白它为什么会发生。是否有某种类型的侦听器可以扩展/实现以查看关闭命令发生的原因?

4

1 回答 1

2

供将来参考:您可以为BeforeShutdown事件定义一个观察者方法,您可以使用该方法至少在关闭之前关闭所有打开的文件等。它不一定会给你一个关闭的原因,但它是一个开始

如果您说 JVM 进程是从外部终止的,那么您可能会在某个地方抛出一个 InterruptedException ,您可以捕获它。请注意,大多数时候捕获 InterruptedException 而不重新中断线程是一个错误。在您的情况下,您应该在调用 Thread#sleep 时有序地关闭它。如果线程被中断,有人想取消执行。他们这样做可能有充分的理由;不要以为你比他们更了解!

至少您可以在执行期间的某个方便点定期检查线程的中断标志,以启动任务的有序关闭。

于 2020-09-22T18:22:17.447 回答