文档

§调度异步任务

您可以调度向 Actor 发送消息和执行任务(函数或 Runnable 实例)。您将获得一个 Cancellable,您可以对其调用 cancel 来取消计划操作的执行。

例如,要每 30 秒向 testActor 发送一条消息

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import javax.inject.Inject
import javax.inject.Named

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext

import org.apache.pekko.actor.ActorRef
import org.apache.pekko.actor.ActorSystem

class MyActorTask @Inject() (actorSystem: ActorSystem, @Named("some-actor") someActor: ActorRef)(
    implicit executionContext: ExecutionContext
) {
  actorSystem.scheduler.scheduleAtFixedRate(
    initialDelay = 0.microseconds,
    interval = 30.seconds,
    receiver = someActor,
    message = "tick"
  )
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.actor.ActorSystem;
import scala.concurrent.ExecutionContext;
import scala.concurrent.duration.Duration;

public class MyActorTask {

  private final ActorRef someActor;
  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public MyActorTask(
      @Named("some-actor") ActorRef someActor,
      ActorSystem actorSystem,
      ExecutionContext executionContext) {
    this.someActor = someActor;
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(0, TimeUnit.SECONDS), // initialDelay
            Duration.create(30, TimeUnit.SECONDS), // interval
            someActor,
            "tick", // message,
            executionContext,
            ActorRef.noSender());
  }
}

注意:有关如何注入 Actor 的信息,请参阅 ScalaJava 文档。

类似地,要从现在起 10 秒后每分钟运行一段代码

Scala
class CodeBlockTask @Inject() (actorSystem: ActorSystem)(implicit executionContext: ExecutionContext) {
  actorSystem.scheduler.scheduleAtFixedRate(initialDelay = 10.seconds, interval = 1.minute) { () =>
    // the block of code that will be executed
    actorSystem.log.info("Executing something...")
  }
}
Java
public class CodeBlockTask {

  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public CodeBlockTask(ActorSystem actorSystem, ExecutionContext executionContext) {
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(10, TimeUnit.SECONDS), // initialDelay
            Duration.create(1, TimeUnit.MINUTES), // interval
            () -> actorSystem.log().info("Running block of code"),
            this.executionContext);
  }
}

或者要从现在起 10 秒后运行一次代码

Scala
class ScheduleOnceTask @Inject() (actorSystem: ActorSystem)(implicit executionContext: ExecutionContext) {
  actorSystem.scheduler.scheduleOnce(delay = 10.seconds) { () =>
    // the block of code that will be executed
    actorSystem.log.info("Executing something...")
  }
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */


  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public CodeBlockOnceTask(ActorSystem actorSystem, ExecutionContext executionContext) {
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleOnce(
            Duration.create(10, TimeUnit.SECONDS), // delay
            () -> System.out.println("Running just once."),
            this.executionContext);
  }
}

您可以查看 Pekko 文档以查看调度程序的其他可能用途。请参阅 pekko.actor.Scheduler 的 Scala 文档Java 文档

注意:您可以创建 CustomExecutionContext,而不是使用默认的 ExecutionContext。请参阅 Java 文档Scala 文档。请参阅下面的相关部分。

§在应用程序启动时启动任务

在定义完上述任务后,您需要在应用程序启动时初始化它们。

§使用 Guice 依赖注入

当使用 Guice 依赖注入时,您需要创建一个模块并启用它,以便将任务加载为 急切单例

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import play.api.inject._
import play.api.inject.SimpleModule

class TasksModule extends SimpleModule(bind[MyActorTask].toSelf.eagerly())
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import com.google.inject.AbstractModule;

public class TasksModule extends AbstractModule {

  @Override
  protected void configure() {
    bind(MyActorTask.class).asEagerSingleton();
  }
}

然后在您的application.conf中添加以下行以启用该模块

play.modules.enabled += "tasks.TasksModule"

由于任务定义与依赖注入框架完全集成,您也可以在其中注入任何必要的组件。有关如何使用 Guice 依赖注入的更多详细信息,请参阅ScalaJava文档。

§使用编译时依赖注入

使用编译时依赖注入时,您只需要在BuiltInComponents的实现中启动它们即可

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import play.api.routing.Router
import play.api.ApplicationLoader.Context
import play.api.BuiltInComponentsFromContext
import play.api.NoHttpFiltersComponents

class MyBuiltInComponentsFromContext(context: Context)
    extends BuiltInComponentsFromContext(context)
    with NoHttpFiltersComponents {
  override def router: Router = Router.empty

  // Task is initialize here
  initialize()

  private def initialize(): Unit = {
    new CodeBlockTask(actorSystem)
  }
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.filters.components.NoHttpFiltersComponents;
import play.routing.Router;

public class MyBuiltInComponentsFromContext extends BuiltInComponentsFromContext
    implements NoHttpFiltersComponents {

  public MyBuiltInComponentsFromContext(ApplicationLoader.Context context) {
    super(context);

    this.initialize();
  }

  private void initialize() {
    // Task is initialize here
    new CodeBlockTask(actorSystem(), executionContext());
  }

  @Override
  public Router router() {
    return Router.empty();
  }
}

然后必须将其与您的自定义ApplicationLoader实现一起使用。有关如何使用编译时依赖注入的更多详细信息,请参阅ScalaJava文档。

§使用CustomExecutionContext

创建执行同步/阻塞工作的任务时,应使用自定义执行上下文。例如,如果您的任务使用 JDBC 访问数据库,则它正在执行阻塞 I/O。如果您使用默认执行上下文,那么您的任务将阻塞用于接收和处理请求的线程。为了避免这种情况,您应该提供一个自定义执行上下文

Scala
import javax.inject.Inject

import org.apache.pekko.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext

class TasksCustomExecutionContext @Inject() (actorSystem: ActorSystem)
    extends CustomExecutionContext(actorSystem, "tasks-dispatcher")
Java
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.apache.pekko.actor.ActorSystem;
import play.libs.concurrent.CustomExecutionContext;
import scala.concurrent.duration.Duration;

public class TasksCustomExecutionContext extends CustomExecutionContext {

  @Inject
  public TasksCustomExecutionContext(ActorSystem actorSystem) {
    super(actorSystem, "tasks-dispatcher");
  }
}

使用tasks-dispatcher作为线程池名称,按照线程池文档中的描述配置线程池,然后将其注入到您的任务中

Scala
class SomeTask @Inject() (actorSystem: ActorSystem, executor: TasksCustomExecutionContext) {
  actorSystem.scheduler.scheduleAtFixedRate(initialDelay = 10.seconds, interval = 1.minute) { () =>
    actorSystem.log.info("Executing something...")
  }(executor) // using the custom execution context
}
Java
public class SomeTask

  private final ActorSystem actorSystem;
  private final TasksCustomExecutionContext executor;

  @Inject
  public SomeTask(ActorSystem actorSystem, TasksCustomExecutionContext executor) {
    this.actorSystem = actorSystem;
    this.executor = executor;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(10, TimeUnit.SECONDS), // initialDelay
            Duration.create(1, TimeUnit.MINUTES), // interval
            () -> actorSystem.log().info("Running block of code"),
            this.executor // using the custom executor
            );
  }
}

§使用第三方模块

您还可以使用模块来调度任务。访问我们的模块目录页面以查看可用模块的列表。

下一步:应用程序关闭


在此文档中发现错误?此页面的源代码可以在此处找到。阅读完文档指南后,请随时贡献拉取请求。有疑问或建议要分享?访问我们的社区论坛,与社区开始对话。