Akka Scheduler:仅在当前运行完成后才运行下一个

时间:2018-06-28 21:46:59

标签: java akka

使用Akka Scheduler安排作业的过程如下(至少从文档中可以看出):

system.scheduler().schedule(
    Duration.Zero(),
    Duration.create(5, TimeUnit.SECONDS),
    workerActor,
    new MessageToTheActor(),
    system.dispatcher(), ActorRef.noSender());

但是我不明白如何确保下一次运行仅在当前运行完成后才能进行。我一直到处都没有成功:(

3 个答案:

答案 0 :(得分:2)

调度程序对于您的用例来说是错误的工具。

另一种选择是Akka Stream的Sink.actorRefWithAck(下面的代码根据链接的文档中的示例改编,并借用此处定义的实用程序类)。您将需要调整工作角色,以处理一些与流状态有关的消息并以确认消息进行回复。确认消息用作背压信号,并指示参与者已准备好处理下一个MessageToTheActor消息。工人演员看起来类似于以下内容:

enum Ack {
  INSTANCE;
}

static class StreamInitialized {}
static class StreamCompleted {}
static class StreamFailure {
  private final Throwable cause;
  public StreamFailure(Throwable cause) { this.cause = cause; }

  public Throwable getCause() { return cause; }
}

public class MyWorker extends AbstractLoggingActor {
  @Override
  public Receive createReceive() {
    return receiveBuilder()
      .match(StreamInitialized.class, init -> {
        log().info("Stream initialized");
        sender().tell(Ack.INSTANCE, self());
      })
      .match(MessageToTheActor.class, msg -> {
        log().info("Received message: {}", msg);
        // do something with the message...
        sender().tell(Ack.INSTANCE, self());
      })
      .match(StreamCompleted.class, completed -> {
        log().info("Stream completed");
      })
      .match(StreamFailure.class, failed -> {
        log().error(failed.getCause(),"Stream failed!");
      })
      .build();
  }
}

要与上述演员一起使用Sink.actorRefWithAck

final ActorSystem system = ActorSystem.create("MySystem");
final Materializer materializer = ActorMaterializer.create(system);

ActorRef workerActor = system.actorOf(Props.create(MyWorker.class, "worker"));

Source<MessageToTheActor, NotUsed> messages = Source.repeat(new MessageToTheActor());

Sink<String, NotUsed> sink = Sink.<String>actorRefWithAck(
  workerActor,
  new StreamInitialized(),
  Ack.INSTANCE,
  new StreamCompleted(),
  ex -> new StreamFailure(ex)
);

messages.runWith(sink, materializer);

请注意使用Source.repeat,在这种情况下,它会不断发出MessageToTheActor消息。使用Sink.actorRefWithAck可确保参与者在处理完当前消息之前不会再收到其他消息。

需要以下导入(显然,Akka Streams依赖项也是如此):

import akka.NotUsed;
import akka.actor.*;
import akka.stream.*;
import akka.stream.javadsl.*;

答案 1 :(得分:1)

调度程序意味着您需要定期进行一些操作,现在,如果second运行取决于您的first运行,那么为什么还要创建一个调度程序。

只需创建两个参与者,one manager actor和另一个child actor

当任务为success时,child actorparent actor发送成功消息,因此父角色要求child actor再执行一次任务。这样可以保证任务以周期性顺序运行,也可以确保前一个任务成功执行。

因此,基本上,您必须在actors的接收方法中实现相应的匹配大小写类。

希望这会有所帮助!

答案 2 :(得分:0)

system.scheduler().schedule(
    Duration.Zero(),
    Duration.create(5, TimeUnit.SECONDS),
    workerActor,
    new MessageToTheActor(),
    system.dispatcher(), ActorRef.noSender());

以上代码表示,调度程序每5秒将message发送给演员workerActor

并且您知道,actor默认仅具有一个线程(除非您使用nr-of-instance> 1进行配置),这意味着发送到workActor的所有消息都将被缓存在mailbox中,因为只有一个线程可以调用receive的{​​{1}}函数。

换句话说,您可以始终确保仅在当前运行完成时才执行下一次运行,因为默认情况下只有一个线程同时为actor工作。