首页 > 解决方案 > Spring Boot:通过控制涉及 MongoClient 的终止顺序来优雅关闭

问题描述

AsyncTaskExecutor我有一个 Spring Boot 应用程序,它使用一个(数量是预定义的) 生成许多线程

线程执行一个无限循环,从一些队列和进程对象中读取,所以我并没有真正的拒绝策略机制(比如接受的 ThreadPool tasks

问题是当应用程序关闭时,线程可能(并且可能)忙于处理一个项目,其中包括使用MongoTemplate.

因此,当应用程序关闭时,MongoClient 会close()自动执行,然后我会从 Mongo 收到一些错误,例如:

java.lang.IllegalStateException: The pool is closed
    at com.mongodb.internal.connection.ConcurrentPool.get(ConcurrentPool.java:137)
    at com.mongodb.internal.connection.DefaultConnectionPool.getPooledConnection(DefaultConnectionPool.java:262)
    at com.mongodb.internal.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:103)
    at com.mongodb.internal.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:92)
    at com.mongodb.internal.connection.DefaultServer.getConnection(DefaultServer.java:85)

如何优雅地关闭应用程序?例如,在不关闭 MongoClient 的同时中断线程?

代码:

豆子创建:

@Bean
AsyncTaskExecutor getTaskExecutor() {
    SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
    return executor;
}

只需通过以下方式执行:

executor.execute(runnable);

标签: javamongodbmultithreadingspring-bootshutdown-hook

解决方案


不要使用SimpleAsyncTaskExecutor- SimpleAsyncTaskExecutor 为每个请求创建一个新线程,而不是使用ThreadPoolTaskExecutor和配置下面提到的两个属性。

/**
 * Set whether to wait for scheduled tasks to complete on shutdown,
 * not interrupting running tasks and executing all tasks in the queue.
 * <p>Default is "false", shutting down immediately through interrupting
 * ongoing tasks and clearing the queue. Switch this flag to "true" if you
 * prefer fully completed tasks at the expense of a longer shutdown phase.
 * <p>Note that Spring's container shutdown continues while ongoing tasks
 * are being completed. If you want this executor to block and wait for the
 * termination of tasks before the rest of the container continues to shut
 * down - e.g. in order to keep up other resources that your tasks may need -,
 * set the {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"}
 * property instead of or in addition to this property.
 * @see java.util.concurrent.ExecutorService#shutdown()
 * @see java.util.concurrent.ExecutorService#shutdownNow()
 */
public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
    this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
}

/**
 * Set the maximum number of seconds that this executor is supposed to block
 * on shutdown in order to wait for remaining tasks to complete their execution
 * before the rest of the container continues to shut down. This is particularly
 * useful if your remaining tasks are likely to need access to other resources
 * that are also managed by the container.
 * <p>By default, this executor won't wait for the termination of tasks at all.
 * It will either shut down immediately, interrupting ongoing tasks and clearing
 * the remaining task queue - or, if the
 * {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"}
 * flag has been set to {@code true}, it will continue to fully execute all
 * ongoing tasks as well as all remaining tasks in the queue, in parallel to
 * the rest of the container shutting down.
 * <p>In either case, if you specify an await-termination period using this property,
 * this executor will wait for the given time (max) for the termination of tasks.
 * As a rule of thumb, specify a significantly higher timeout here if you set
 * "waitForTasksToCompleteOnShutdown" to {@code true} at the same time,
 * since all remaining tasks in the queue will still get executed - in contrast
 * to the default shutdown behavior where it's just about waiting for currently
 * executing tasks that aren't reacting to thread interruption.
 * @see java.util.concurrent.ExecutorService#shutdown()
 * @see java.util.concurrent.ExecutorService#awaitTermination
 */
public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
    this.awaitTerminationSeconds = awaitTerminationSeconds;
}

相关部分

设置此执行程序在关闭时应该阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余任务完成执行。如果您的剩余任务可能需要访问也由容器管理的其他资源,这将特别有用。

您可以使用 spring 自动配置来控制任务执行属性(首选)或使用@Bean注释以编程方式进行配置

2.1.0 中的 Spring Boot 为任务执行器提供自动配置,并同时用于@EnableAsyncSpring MVC Async 支持。

应用程序不需要任务执行器 bean / webMvcConfigurer 配置。所以删除你有的,应该是好的。

您可以使用带有spring.task.execution.*.

spring.task.execution.shutdown.await-termination=true
spring.task.execution.shutdown.await-termination-period=60

可以找到完整列表here

更多细节herehere

或者

@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.setMaxPoolSize(5); 
    taskExecutor.waitForTasksToCompleteOnShutdown(true);
    taskExecutor.setAwaitTerminationSeconds(60);
    return taskExecutor;
  }

推荐阅读