You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch在Fargate任务停止时出现EntityManagerFactory已关闭错误

解决Spring Batch异步作业完成后关闭Fargate任务时的EntityManagerFactory错误

问题根源

使用异步JobLauncher提交作业时,若在JobCompletionListener里直接触发任务停止,会导致Spring上下文被提前强制关闭,而异步作业的残留线程此时仍在尝试访问已关闭的EntityManagerFactory,最终抛出错误。核心矛盾是监听器触发时机早于异步作业资源的完全释放。

正确实现方案

1. 等待异步作业完全结束后再关闭应用

不要直接在监听器里停止任务,而是先确保所有异步作业线程执行完毕,再优雅关闭Spring上下文。可以通过监听作业执行事件+线程池同步的方式实现:

@Configuration
public class BatchJobShutdownConfig {

    @Autowired
    private JobLauncher asyncJobLauncher;

    @Autowired
    private ApplicationContext applicationContext;

    @EventListener
    public void onJobCompletion(JobExecutionEvent event) {
        if (BatchStatus.COMPLETED.equals(event.getJobExecution().getStatus())) {
            waitForAsyncTasks();
            SpringApplication.exit(applicationContext, () -> 0);
        }
    }

    private void waitForAsyncTasks() {
        AsyncTaskExecutor executor = ((SimpleJobLauncher) asyncJobLauncher).getTaskExecutor();
        if (executor instanceof ThreadPoolTaskExecutor threadPool) {
            threadPool.shutdown();
            try {
                if (!threadPool.awaitTermination(5, TimeUnit.MINUTES)) {
                    threadPool.shutdownNow();
                }
            } catch (InterruptedException e) {
                threadPool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
}

2. 开启Spring Boot优雅关闭配置

在application.properties中配置优雅关闭参数,保证容器关闭时资源能有序释放:

# 给关闭阶段设置超时时间,确保资源释放完成
spring.lifecycle.timeout-per-shutdown-phase=30s
# 启用优雅关闭模式
spring.main.shutdown=graceful

3. 依赖ECS自动停止任务,而非手动调用API

Fargate任务的主进程退出后,ECS会自动终止任务,因此无需在应用内调用StopTask API。只要保证Spring Boot应用优雅退出,ECS会自动处理任务停止。

如果必须手动触发任务停止,需在异步作业完全结束后再调用AWS SDK:

@Autowired
private AmazonECS ecsClient;

private void stopCurrentEcsTask() {
    String taskArn = System.getenv("ECS_TASK_ARN");
    StopTaskRequest request = new StopTaskRequest()
            .withTask(taskArn)
            .withReason("Batch job completed successfully");
    ecsClient.stopTask(request);
}

注意:需给ECS任务角色添加ecs:StopTask权限。

4. 调整JobCompletionListener逻辑(备选方案)

若坚持使用监听器,可通过信号量让主线程等待作业完成后再关闭应用:

@Component
public class JobCompletionSignalListener implements JobCompletionListener {

    private final CountDownLatch shutdownLatch = new CountDownLatch(1);

    @Override
    public void afterJob(JobExecution jobExecution) {
        if (BatchStatus.COMPLETED.equals(jobExecution.getStatus())) {
            shutdownLatch.countDown();
        }
    }

    public void awaitShutdown() throws InterruptedException {
        shutdownLatch.await();
    }
}

在主类中等待信号:

@SpringBootApplication
public class BatchApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(BatchApplication.class, args);
        JobCompletionSignalListener listener = context.getBean(JobCompletionSignalListener.class);
        try {
            listener.awaitShutdown();
            SpringApplication.exit(context, () -> 0);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

关键注意事项

  • 异步JobLauncher的线程池需合理配置核心线程数、最大线程数,确保作业完成后线程能被正确回收。
  • 绝对不能在作业执行过程中强制关闭Spring上下文,必须等待所有异步任务的资源释放完成。
  • 优先依赖ECS的进程退出自动终止机制,减少手动调用API带来的上下文冲突风险。

内容的提问来源于stack exchange,提问作者shoaib-jpeg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:15:02