Play Framework + Akka:如何避免在关闭应用程序时执行计划任务

Posted

技术标签:

【中文标题】Play Framework + Akka:如何避免在关闭应用程序时执行计划任务【英文标题】:Playframework + Akka: how to avoid excuting scheduled tasks executed when shutdown application 【发布时间】:2016-06-12 06:01:22 【问题描述】:

我在播放应用程序服务器启动时启动的调度程序有问题,但是一旦应用程序关闭,它就会命中以下代码部分:

// firstDay something like 1 = monday 
private void startScheduler(final ImageService imageService,
                            final ActorSystem system) 
    startImagesCleanupScheduler(imageService, system);
    Logger.info("Schedulers started");

我的问题是Runnable 块立即开始执行而不是取消任务。

澄清代码:

以下方法启动Scheduler

private void startImagesCleanupScheduler(ImageService imageService, ActorSystem system) 
    system.scheduler().schedule(
            Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
            Duration.create(1, TimeUnit.DAYS),     //Frequency 1 days
            () -> 
                int rows = imageService.cleanupInactiveImages();
                Logger.info(String.format("%d inactive unused images cleaned from db", rows));

            ,
            system.dispatcher()
    );

我关机时的日志请注意这里的第一行:

[info] - application - 1 inactive unused images cleaned from db
[info] - application - Shutting down connection pool.
[info] - application - Creating Pool for datasource 'default'
...
[info] - application - Schedulers started
[info] - play.api.Play - Application started (Prod)

您可以看到它执行调度程序时忽略了原来的执行时间,然后关闭,然后启动,之后“调度程序启动”。

什么问题,我如何取消调度程序或阻止播放在关机前运行它?这是 Akka 的错误吗?

我在OnStartup 内打电话给startScheduler,就像以下问题的答案一样:

java Playframework GlobalSettings deprecation for onStart

编辑: 以下是重现问题的最少代码:

首先创建OnStartup类:

@Singleton
public class OnStartup 

    @Inject
    public OnStartup(final ActorSystem system) 
        startScheduler(system);
    

    private void startScheduler(final ActorSystem system) 
        startImagesCleanupScheduler(system);
        Logger.info("Schedulers started");
    

    private void startImagesCleanupScheduler(ActorSystem system) 
        system.scheduler().schedule(
                Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
                Duration.create(1, TimeUnit.DAYS),     //Frequency 1 days
                () -> 
                    //int rows = imageService.cleanupInactiveImages();
                    rows = 1;
                    Logger.info(String.format("%d inactive unused images cleaned from db", rows ));
                ,
                system.dispatcher()
        );
    


然后创建模块:

public class OnStartupModule extends AbstractModule 
    @Override
    public void configure() 
        bind(OnStartup.class).asEagerSingleton();
    

最后在 application.conf 中启用模块:

play.modules.enabled += "modules.OnStartupModule"

【问题讨论】:

您能否也发布用于启动此计划的代码,以防万一,因为这看起来没问题? 已经发过问题你可以参考***.com/questions/35842293/…在启动时创建,然后看这里的代码,只需要打印日志,注释掉服务调用 @AleksandarStojadinovic 我提供了简单的代码 目前我只看到您不需要@Singleton 注释,因为您正在模块中配置它。这意味着它被延迟实例化,而模块将其声明为渴望。我不相信这是原因,因为我不知道注释和模块之间的优先级,但试试吧。 我试过了,但似乎是它的 Akka 系统问题,看看这个:doc.akka.io/docs/akka/current/scala/…,所以我现在正在寻找一种解决方法。 【参考方案1】:

这对于评论来说有点太长了,但我没有测试这种方法,您可能需要对其进行调整。文档说组件按照创建它们的相反顺序被销毁。这意味着您可能会在 ActorSystem 关闭之前取消您的调度程序,因为该类是在它之后注册的并且依赖于它。在您的 OnStartup 类中创建一个这样的关闭挂钩,您将在其中取消您的日程安排。这意味着在ActorSystem 关闭时,将没有计划执行:

@Singleton
public class OnStartup 

    private final Cancellable cancellableSchedule;

    @Inject
    public OnStartup(final ActorSystem system, final ApplicationLifecycle l) 
        cancellableSchedule = startScheduler(system);
        initStopHook(l);
    

    private Cancellable startScheduler(final ActorSystem system) 
        return startImagesCleanupScheduler(system);
        Logger.info("Schedulers started");
    

    private Cancellable startImagesCleanupScheduler(ActorSystem system) 
        return system.scheduler().schedule(
            Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay
            Duration.create(1, TimeUnit.DAYS),     //Frequency 1 days
            () -> 
                //int rows = imageService.cleanupInactiveImages();
                rows = 1;
                Logger.info(String.format("%d inactive unused images cleaned from db", rows ));
                ,
            system.dispatcher()
        );
    

    private void initStopHook(ApplicationLifecycle lifecycle) 
       lifecycle.addStopHook(() -> 
            cancellableSchedule.cancel();
        return CompletableFuture.completedFuture(null);
        );
     


【讨论】:

非常感谢,我会尽快尝试 这个解决方案好运吗? 好吧,今天我休息了 4 天,今天我又开始工作了,编码但没有再次尝试,我会在 1 小时内测试 :) 我试过了,是的,解决了这个问题,但有一些修复,它的.cancel()而不是stop(),我做了一个cancellableSchedule作为列表,我添加了可取消的实例安排到该列表中并对其进行迭代。 (因为我启动了 2 个调度程序) 太棒了。我将把 stop() 换成 cancel() 。至于列表,它没有包含在原始问题中,因此为了将来参考,最好将其省略。

以上是关于Play Framework + Akka:如何避免在关闭应用程序时执行计划任务的主要内容,如果未能解决你的问题,请参考以下文章

在 Play Framework 2.4 中为 Scala 实现 Akka

Play Framework 2.5 JavaAsync 抛出 CompletionException

如何在 Play 2.2.3 项目中使用 Spark 1.2.0,因为它因 NoSuchMethodError:akka.util.Helpers 而失败?

可以同时使用 Play 1.x 和 Akka 吗?

在 Akka actor 中玩 2.4 依赖注入

Play Framework:如何不查看文件夹以了解 Play Framework 中的更改