关闭时停止所有spring批处理作业(ctrl-c)

w3nuxt5m  于 2021-07-23  发布在  Java
关注(0)|答案(1)|浏览(353)

我有一个springboot/spring批处理应用程序,它启动不同的作业。
当应用程序停止时( CTRL-C )作业保持在运行状态(已启动)。
尽管 CTRL-C 让应用程序有足够的时间优雅地停止作业结果与 kill -9 .
我找到了一种方法(见下文),可以在应用程序被终止时优雅地停止所有作业 CTRL-C ,但想知道是否有更好/更简单的方法来实现这一目标。
下面的所有内容都是关于我如何设法停止工作的文档。
在一篇来自부알프레도 一 JobExecutionListener 用于注册应停止作业的关闭挂钩:

public class ProcessShutdownListener implements JobExecutionListener {
    private final JobOperator jobOperator;
    ProcessShutdownListener(JobOperator jobOperator) { this.jobOperator = jobOperator; }

    @Override public void afterJob(JobExecution jobExecution) { /* do nothing. */ }

    @Override
    public void beforeJob(final JobExecution jobExecution) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    jobOperator.stop(jobExecution.getId());
                    while(jobExecution.isRunning()) {
                        try { Thread.sleep(100); } catch (InterruptedException e) {}
                    }
                } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) { /* ignore */ }
            }
        });
    }
}

除了提供的代码,我还必须创建一个 JobRegistryBeanPostProcessor .
没有这个后处理器 jobOperator 找不到工作。
( NoSuchJobException: No job configuration with the name [job1] was registered ```
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}

关闭挂钩无法将状态写入数据库,因为数据库连接已关闭: `org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL)` ```
Processing item 2 before
Shutdown Hook is running !
2021-02-08 22:39:48.950  INFO 12676 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-02-08 22:39:49.218  INFO 12676 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
Processing item 3 before
Exception in thread "Thread-3" org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30004ms.

为了确保springboot在停止作业之前不会关闭hikari数据源池,我使用了 SmartLifeCycle 正如这里提到的。
决赛 ProcessShutdownListener 看起来像:

@Component
public class ProcessShutdownListener implements JobExecutionListener, SmartLifecycle {
    private final JobOperator jobOperator;
    public ProcessShutdownListener(JobOperator jobOperator) { this.jobOperator = jobOperator; }

    @Override
    public void afterJob(JobExecution jobExecution) { /* do nothing. */ }

    private static final List<Runnable> runnables = new ArrayList<>();

    @Override
    public void beforeJob(final JobExecution jobExecution) {
        runnables.add(() -> {
                try {
                    if (!jobOperator.stop(jobExecution.getId())) return;
                    while (jobExecution.isRunning()) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException ignored) { /* ignore */ }
                    }
                } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) { /* ignore */ }
            });
    }

    @Override
    public void start() {}

    @Override
    public void stop() {
//        runnables.stream()
//                .parallel()
//                .forEach(Runnable::run);
        runnables.forEach(Runnable::run);
    }

    @Override
    public boolean isRunning() { return true; }

    @Override
    public boolean isAutoStartup() { return true; }

    @Override
    public void stop(Runnable callback) { stop(); callback.run(); }

    @Override
    public int getPhase() { return Integer.MAX_VALUE; }
}

配置作业时必须注册此侦听器:

@Bean
    public Job job(JobBuilderFactory jobs,
                   ProcessShutdownListener processShutdownListener) {
        return jobs.get("job1")
                .listener(processShutdownListener)
                .start(step(null))
                .build();
    }

最后,如异常输出中所述,输出标志: ;DB_CLOSE_ON_EXIT=FALSE 必须添加到jdbc url。

5m1hhzi4

5m1hhzi41#

这种方法是可行的,因为据我所知,关闭钩子是jvm提供的截获外部信号的唯一方法。但是,这种方法不能保证工作,因为jvm不能保证调用关闭挂钩。下面是一段摘自javadoc的 Runtime.addShutdownHook 方法:

In rare circumstances the virtual machine may abort, that is, stop running
without shutting down cleanly. This occurs when the virtual machine is 
terminated externally, for example with the SIGKILL signal on Unix or 
the TerminateProcess call on Microsoft Windows.

此外,关闭挂钩预计将“快速”运行:

Shutdown hooks should also finish their work quickly. When a program invokes
exit the expectation is that the virtual machine will promptly shut down
and exit.

就你而言, JobOperator.stop 涉及一个数据库事务(可能跨网络)来更新作业的状态,我不确定这个操作是否足够快。
作为补充说明,在samples模块中有一个名为gracefulshutdownfunctionaltests的示例。这个例子基于 JobExecution.stop 已弃用,但将更新为使用 JobOperator.stop .

相关问题