如何优雅地结束 spring @Schedule 任务?

2024-02-20

我正在尝试让 Spring Boot 服务优雅地结束。它有一个方法@Scheduled注解。该服务使用 spring-data 作为数据库,使用 spring-cloud-stream 作为 RabbitMQ。在计划的方法结束之前,数据库和 RabbitMQ 的可访问性至关重要。有一个自动缩放器,可以频繁启动/停止服务实例,在停止时崩溃是不可能的。

从这篇文章Spring - 计划任务 - 优雅关机 https://stackoverflow.com/questions/43664259/spring-scheduled-task-graceful-shutdown我认为添加应该足够了

    @Bean
    TaskSchedulerCustomizer taskSchedulerCustomizer() {
        return taskScheduler -> {
            taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
            taskScheduler.setAwaitTerminationSeconds(30);
        };
    }

spring 应该等待关闭应用程序,直到预定的方法完成或 30 秒到期。

当服务在执行计划方法时停止时,我可以从日志中看到以下内容

  1. spring-cloud-stream 正在关闭连接而不等待方法完成。
  2. spring-data 也会立即关闭数据库连接。
  3. 该方法不会停止并尝试完成但失败,因为它无法再访问数据库。

有什么想法如何让包括 db-connection 和rabbitMq 访问在内的预定方法优雅地完成吗?

这是我的应用程序类:

@SpringBootApplication(scanBasePackages = {
    "xx.yyy.infop.dao",
    "xx.yyy.infop.compress"})
@EntityScan("ch.sbb.infop.common.entity")
@EnableJpaRepositories({"xx.yyy.infop.dao", "xx.yyy.infop.compress.repository"})
@EnableBinding(CompressSink.class)
@EnableScheduling
public class ApplicationCompress {

    @Value("${max.commpress.timout.seconds:300}")
    private int maxCompressTimeoutSeconds;

    public static void main(String[] args) {
        SpringApplication.run(ApplicationCompress.class, args);
    }

    @Bean
    TaskSchedulerCustomizer taskSchedulerCustomizer() {
        return taskScheduler -> {
            taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
            taskScheduler.setAwaitTerminationSeconds(maxCompressTimeoutSeconds);
        };
    }

}

这是豆子:

@Component
@Profile("!integration-test")
public class CommandReader {

    private static final Logger LOGGER = LoggerFactory.getLogger(CommandReader.class);

    private final CompressSink compressSink;
    private final CommandExecutor commandExecutor;

    CommandReader(CompressSink compressSink, CommandExecutor commandExecutor) {
        this.compressSink = compressSink;
        this.commandExecutor = commandExecutor;
    }

    @PreDestroy
    private void preDestory() {
        LOGGER.info("preDestory");
    }

    @Scheduled(fixedDelay = 1000)
    public void poll() {
        LOGGER.debug("Start polling.");
        ParameterizedTypeReference<CompressCommand> parameterizedTypeReference = new ParameterizedTypeReference<>() {
        };
        if (!compressSink.inputSync().poll(this::execute, parameterizedTypeReference)) {
            compressSink.inputAsync().poll(this::execute, parameterizedTypeReference);
        }
        LOGGER.debug("Finished polling.");
    }

    private void execute(Message<?> message) {
        CompressCommand compressCommand = (CompressCommand) message.getPayload();

        // uses spring-data to write to DB
        CompressResponse compressResponse = commandExecutor.execute(compressCommand);

        // Schreibt die Anwort in Rensponse-Queue
        compressSink.outputResponse().send(MessageBuilder.withPayload(compressResponse).build());
    }

}

这里是日志中的一些行(参见https://pastebin.com/raw/PmmqhH1P https://pastebin.com/raw/PmmqhH1P完整日志):

2020-05-15 11:59:35,640 [restartedMain] - INFO org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler.initialize - traceid= - Initializing ExecutorService 'taskScheduler'

2020-05-15 11:59:44,976 [restartedMain] - INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.initialize - traceid= - Initializing ExecutorService 'applicationTaskExecutor'

Disconnected from the target VM, address: '127.0.0.1:52748', transport: 'socket'
2020-05-15 12:00:01,228 [SpringContextShutdownHook] - INFO org.springframework.cloud.stream.binder.BinderErrorChannel.adjustCounterIfNecessary - traceid= - Channel 'application-1.kompressSync.komprimierungSyncProcessingGroup.errors' has 1 subscriber(s).
2020-05-15 12:00:01,229 [SpringContextShutdownHook] - INFO org.springframework.cloud.stream.binder.BinderErrorChannel.adjustCounterIfNecessary - traceid= - Channel 'application-1.kompressSync.komprimierungSyncProcessingGroup.errors' has 0 subscriber(s).
2020-05-15 12:00:01,232 [SpringContextShutdownHook] - INFO org.springframework.cloud.stream.binder.BinderErrorChannel.adjustCounterIfNecessary - traceid= - Channel 'application-1.kompressAsync.komprimierungAsyncProcessingGroup.errors' has 1 subscriber(s).
2020-05-15 12:00:01,232 [SpringContextShutdownHook] - INFO org.springframework.cloud.stream.binder.BinderErrorChannel.adjustCounterIfNecessary - traceid= - Channel 'application-1.kompressAsync.komprimierungAsyncProcessingGroup.errors' has 0 subscriber(s).
2020-05-15 12:00:01,237 [SpringContextShutdownHook] - INFO org.springframework.integration.endpoint.EventDrivenConsumer.logComponentSubscriptionEvent - traceid= - Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2020-05-15 12:00:01,237 [SpringContextShutdownHook] - INFO org.springframework.integration.channel.PublishSubscribeChannel.adjustCounterIfNecessary - traceid= - Channel 'application-1.errorChannel' has 0 subscriber(s).
2020-05-15 12:00:01,237 [SpringContextShutdownHook] - INFO org.springframework.integration.endpoint.EventDrivenConsumer.stop - traceid= - stopped bean '_org.springframework.integration.errorLogger'
2020-05-15 12:00:01,244 [SpringContextShutdownHook] - INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.shutdown - traceid= - Shutting down ExecutorService 'applicationTaskExecutor'
2020-05-15 12:00:01,245 [SpringContextShutdownHook] - INFO yy.xxx.infop.compress.CommandReader.preDestory - traceid= - preDestory
2020-05-15 12:00:01,251 [SpringContextShutdownHook] - INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.destroy - traceid= - Closing JPA EntityManagerFactory for persistence unit 'default'
2020-05-15 12:00:01,256 [SpringContextShutdownHook] - INFO org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler.shutdown - traceid= - Shutting down ExecutorService 'taskScheduler'
2020-05-15 12:00:01,256 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 4
2020-05-15 12:00:02,257 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 5
2020-05-15 12:00:03,258 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 6
2020-05-15 12:00:04,260 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 7
2020-05-15 12:00:05,260 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 8
2020-05-15 12:00:06,261 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - 9
2020-05-15 12:00:07,262 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.doInTransactionWithoutResult - traceid=d22b696edc90e123 - end
2020-05-15 12:00:07,263 [scheduling-1] - INFO yy.xxx.infop.compress.condense.VmLaufVerdichter.verdichte - traceid=d22b696edc90e123 - VarianteTyp=G, vmId=482392382, vnNr=8416
2020-05-15 12:00:07,326 [scheduling-1] -ERROR yy.xxx.infop.compress.CommandExecutor.execute - traceid=d22b696edc90e123 - 

org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'inMemoryDatabaseShutdownExecutor': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:208) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]

2020-05-15 12:00:08,332 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.execute - traceid=d22b696edc90e123 - Compress started. compressCommand=yy.xxx.infop.compress.client.CompressCommand@247ec0d[hostName=K57176,jobId=b1211ee8-4a54-47f2-a58b-92b3560bbddd,cmdId=1,userId=goofy2,commandTyp=verdichtet G, T und komprimiert G, T,vmId=482392382,started=1589536752609]
2020-05-15 12:00:08,337 [scheduling-1] -ERROR yy.xxx.infop.compress.CommandExecutor.execute - traceid=d22b696edc90e123 - 

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:448) ~[spring-orm-5.2.4.RELEASE.jar:5.2.4.RELEASE]

2020-05-15 12:00:10,339 [scheduling-1] - INFO yy.xxx.infop.compress.CommandExecutor.execute - traceid=d22b696edc90e123 - Compress started. compressCommand=yy.xxx.infop.compress.client.CompressCommand@247ec0d[hostName=K57176,jobId=b1211ee8-4a54-47f2-a58b-92b3560bbddd,cmdId=1,userId=goofy2,commandTyp=verdichtet G, T und komprimiert G, T,vmId=482392382,started=1589536752609]
2020-05-15 12:00:10,343 [scheduling-1] -ERROR yy.xxx.infop.compress.CommandExecutor.execute - traceid=d22b696edc90e123 - 

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed

2020-05-15 12:00:10,351 [scheduling-1] -DEBUG yy.xxx.infop.compress.CommandReader.poll - traceid=d22b696edc90e123 - Finished polling.
2020-05-15 12:00:10,372 [SpringContextShutdownHook] - INFO org.springframework.integration.monitor.IntegrationMBeanExporter.destroy - traceid= - Summary on shutdown: bean 'response'
2020-05-15 12:00:10,372 [SpringContextShutdownHook] - INFO org.springframework.integration.monitor.IntegrationMBeanExporter.destroy - traceid= - Summary on shutdown: nullChannel
2020-05-15 12:00:10,373 [SpringContextShutdownHook] - INFO org.springframework.integration.monitor.IntegrationMBeanExporter.destroy - traceid= - Summary on shutdown: bean 'errorChannel'
2020-05-15 12:00:10,373 [SpringContextShutdownHook] - INFO org.springframework.integration.monitor.IntegrationMBeanExporter.destroy - traceid= - Summary on shutdown: bean '_org.springframework.integration.errorLogger.handler' for component '_org.springframework.integration.errorLogger'
2020-05-15 12:00:10,374 [SpringContextShutdownHook] - INFO com.zaxxer.hikari.HikariDataSource.close - traceid= - HikariPool-1 - Shutdown initiated...
2020-05-15 12:00:10,405 [SpringContextShutdownHook] - INFO com.zaxxer.hikari.HikariDataSource.close - traceid= - HikariPool-1 - Shutdown completed.

Process finished with exit code 130

我已经测试过这个配置,它应该与你的配置相同TaskSchedulerCustomizer:

spring.task.scheduling.shutdown.await-termination=true
spring.task.scheduling.shutdown.await-termination-period=30s

如果有活动任务,Spring 会在所有可用服务等待 30 秒,然后再关闭任何服务。如果没有活动任务,则立即关闭。

值得一提的是,让我想到这个问题的是优雅的关闭@Async方法的配置方式非常相似:

spring.task.execution.shutdown.await-termination=true
spring.task.execution.shutdown.await-termination-period=1s

或者在代码中:

@Bean
public TaskExecutorCustomizer taskExecutorCustomizer() {
    // Applies to @Async tasks, not @Scheduled as in the question
    return (customizer) -> {
        customizer.setWaitForTasksToCompleteOnShutdown(true);
        customizer.setAwaitTerminationSeconds(10);
    };
}

回到你的例子,我的猜测是TaskSchedulerCustomizer并未实际执行或执行后被其他内容覆盖。

对于第一个选项,通过添加日志语句或设置断点来验证taskSchedulerCustomizer().

对于第二个选项,我建议在TaskSchedulerBuilder::configure()看看会发生什么。一旦调试器在该方法中中断,就在ExecutorConfigurationSupport::awaitTerminationMillis的财产taskScheduler查看该属性是否在其他地方被修改。

可以在方法中看到关闭过程中使用的最终终止时间ExecutorConfigurationSupport::awaitTerminationIfNecessary.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何优雅地结束 spring @Schedule 任务? 的相关文章

随机推荐

  • 为什么inet_ntoa中的缓冲区大小是18?

    我查看了的实现inet ntoa like this https code woboq org userspace glibc inet inet ntoa c html and this https android googlesourc
  • Haskell Stack Ghci 测试套件

    我正在尝试使用堆栈在 ghci 中加载我的测试套件并让它加载 QuickCheck 和 hspec 依赖项 我怎样才能做到这一点 我正在使用 Franklinchen 模板 https github com commercialhaskel
  • 从 QML 访问 QList 时 QT/QML C++ 程序崩溃

    我有 2 个用于数据处理的类 CGameList 和 Game 我在 qml 中定义了一个 GameList gamelist 对象来使用它 我有一个列表视图 显示此游戏列表中的游戏 editGames open 如果我单击此列表中的一个条
  • 朱莉娅中固定大小数组的最佳传递维度

    我想编写一个以矩阵作为输入的函数 这是复杂项目中频繁的低级调用 因此使该函数尽可能快可能会产生严重的性能影响 因为速度对我来说非常重要 所以我使用以下类型FixedSizeArrays据我所知 这将节省内存使用 但我经常知道输入矩阵的某些属
  • JMeter 使用什么 Maven 插件? jmeter-maven-plugin 还是 chronos-jmeter-maven-plugin?

    我需要设置由 CI 系统自动触发运行的性能测试 为此 我想使用 JMeter 因为已经存在一些脚本和经验 并且我想将其与 Maven 结合起来 在我对合理插件的研究过程中 我发现存在两个插件 jmeter maven 插件 http wik
  • 无论我输入什么参数,速度函数都不会改变海龟的位置

    我制作的程序有两只海龟 一只是用户 玩家 另一只是玩家 2 它们通过一个名为 checkcollision 的函数运行 该函数确定海龟是否相交 从而将第二只海龟的 x 和 250 250 移动到 250 250 的随机位置y 坐标 然而问题
  • BULK INSERT 失败,行终止符位于最后一行

    我正在将使用 cygwin shell 命令编译的 CSV 导入 MS SQL 2014 使用 BULK INSERT import from D tail csv WITH FIELDTERMINATOR ROWTERMINATOR r
  • 如何使用 QSettings 在 Qt 应用程序中加载设置

    有两种可能的方法 将所有设置加载到某个结构中 按需加载值 哪种方法更好 这取决于您将如何使用您的设置文件 您是否希望允许您的应用程序的用户动态地更改文件中的设置 例如 ini 文件 或者必须通过 GUI 来设置设置 如果您使用某些 GUI
  • d3.js v5 - Promise.all 替换 d3.queue

    我已经使用 d3 js v4 一段时间了 我了解到 Mike Bostock 已将 v5 版本中的 d3 queue 替换为 Promise 原生 JavaScript 对象 我想与您核实一下我编写的这段代码是否正确地 异步 这些 URL
  • Java 中 Date(String s) 的未弃用的完全等效项?

    我有旧代码使用new Date dateString 解析日期字符串 编译代码会产生弃用警告Date java lang String in java util Date has been deprecated javadoc无益地建议我使
  • 带有 lxml 子路径的 XPath 谓词?

    我试图理解发送给我的用于 ACORD XML 表单 保险中的常见格式 的 XPath 他们发给我的 XPath 是 为了简洁而被截断 PersApplicationInfo InsuredOrPrincipal InsuredOrPrinc
  • Hudson -CI 屏幕保护程序设置

    您好 有没有我可以设置一个屏幕保护程序 其中包含在 hudson 中运行的项目列表 该列表指示项目的状态 假设屏幕保护程序的部分表示项目成功 则显示绿色 如果项目构建失败 则显示红色 可能屏幕保护程序必须分区到多个项目 您可以在任何合适的环
  • 将一个时间序列分割为另一个不规则时间序列

    我正在尝试用一个独特的不规则时间序列分割多个 xts 对象 split xts按天 分钟 秒等进行分割 使用断点需要相等长度的向量 当我尝试分割数据时 这会产生错误 dd lt c 2014 02 23 2014 03 12 2014 05
  • 如何正确地将 IsPressed 属性绑定到我的命令参数?

    我制作了一个自定义按钮来将命令绑定到 自定义 路由 IsPressedChanged事件 以便在按下按钮和释放按钮时执行该命令
  • Python - TypeError:“int64”类型的对象不可 JSON 序列化

    我有一个存储商店名称和每日销售额的数据框 我正在尝试使用下面的 Python 脚本将其插入到 Salesforce 但是 我收到以下错误 TypeError Object of type int64 is not JSON serializ
  • Windows 10 上的 VersionNT MSI 属性

    我发现当我更新引导程序的清单以支持 Windows 10 兼容性时 MSI 的 InstallUISequence 将正确设置 VersionNT 1000 但 InstallExecuteSequence 将设置 VersionNT 60
  • 将 UserID 从 ASP.Net 安全地传递到 Javascript

    在我当前正在开发的应用程序中 我们使用 ASP Net 表单身份验证来授予用户对站点的进一步访问权限 该网站面向移动用户 因此我们试图尽可能摆脱服务器的束缚 并利用 KnockoutJS 进行 Web 服务调用并加载数据 以便用户可以查看它
  • Azure 存储将 blob 移动到其他容器

    我正在寻找一种将 Azure 中的 blob 从一个容器移动到另一个容器的方法 我找到的唯一解决方案是使用 Azure 存储数据移动库 但这似乎适用于不同帐户 我想将同一帐户内的 blob 移动到另一个容器 我没用过Azure Storag
  • JUnit。并行运行。但所有测试方法都处理单例实例。怎么解决?

    所以 我有几个JUnit类 每个类都包含一个测试方法列表 每个方法都是相互独立的 没有直接的联系 但我们有间接联系 所有方法都处理一个单例对象 它是Selenium Web Driver实例 是的 我用1Web Driver我所有测试的实例
  • 如何优雅地结束 spring @Schedule 任务?

    我正在尝试让 Spring Boot 服务优雅地结束 它有一个方法 Scheduled注解 该服务使用 spring data 作为数据库 使用 spring cloud stream 作为 RabbitMQ 在计划的方法结束之前 数据库和