akka版本2.6.9
版权声明:本文为博主原创文章,未经博主允许不得转载。
要使用Akka Actor类型,您必须在项目中添加以下依赖项:
<properties>
<akka.version>2.6.10</akka.version>
<scala.binary.version>2.13</scala.binary.version>
</properties>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor-typed_${scala.binary.version}</artifactId>
<version>${akka.version}</version>
</dependency>
介绍
Actor是必须显式启动和停止的有状态资源。
重要的是要注意,当不再引用Actor时,Actor不会自动停止,创建的每个Actor也必须显式地销毁。惟一的简化是,停止父Actor还将递归地停止该父Actor创建的所有子Actor。当角色系统关闭时,所有的角色也会自动停止。
请注意
ActorSystem是重量级结构,它将分配线程,因此为每个逻辑应用程序创建一个线程。通常每个JVM进程有一个actor系统。
创建角色
一个actor可以创建或衍生任意数量的子actor,而子actor又可以衍生自己的子actor,从而形成一个actor层次结构。ActorSystem托管层次结构,并且只能有一个根actor,即位于ActorSystem层次结构顶端的actor。子角色的生命周期与父角色相关联——子角色可以停止自己或在任何时候被停止,但它永远无法活过父角色。
ActorContext
ActorContext可以被多种目的访问,例如:
如果一个行为需要使用ActorContext,例如衍生子actor,或者使用context.getSelf(),它可以通过用Behaviors.setup包装构造来获得:
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {
public static Behavior<SayHello> create() {
return Behaviors.setup(HelloWorldMain::new);
}
private final ActorRef<HelloWorld.Greet> greeter;
private HelloWorldMain(ActorContext<SayHello> context) {
super(context);
greeter = context.spawn(HelloWorld.create(), "greeter");
}
}
ActorContext线程安全
ActorContext中的许多方法都不是线程安全的
守护actor
顶级Actor,也称为用户监护actor,是与ActorSystem一起创建的。发送到actor系统的消息被定向到根actor。根actor是由用来创建ActorSystem的行为定义的,在下面的例子中命名为HelloWorldMain:
final ActorSystem<HelloWorldMain.SayHello> system =
ActorSystem.create(HelloWorldMain.create(), "hello");
system.tell(new HelloWorldMain.SayHello("World"));
system.tell(new HelloWorldMain.SayHello("Akka"));
对于非常简单的应用程序,guardian可能包含实际的应用程序逻辑和处理消息。一旦应用程序处理了不止一个问题,监护人就应该引导应用程序,将各种子系统作为子系统生成,并监视它们的生命周期。
当守护actor停止时,将停止actor系统。
当ActorSystem。协调关闭流程将以特定的顺序停止actor和服务。
生成子actor
子actor是通过ActorContext的衍生来创建和启动的。在下面的示例中,当根actor启动时,它会生成一个由HelloWorld行为描述的子actor。此外,当根actor接收到SayHello消息时,它会创建一个由行为HelloWorldBot定义的子actor:
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {
public static class SayHello {
public final String name;
public SayHello(String name) {
this.name = name;
}
}
public static Behavior<SayHello> create() {
return Behaviors.setup(HelloWorldMain::new);
}
private final ActorRef<HelloWorld.Greet> greeter;
private HelloWorldMain(ActorContext<SayHello> context) {
super(context);
greeter = context.spawn(HelloWorld.create(), "greeter");
}
@Override
public Receive<SayHello> createReceive() {
return newReceiveBuilder().onMessage(SayHello.class, this::onStart).build();
}
private Behavior<SayHello> onStart(SayHello command) {
ActorRef<HelloWorld.Greeted> replyTo =
getContext().spawn(HelloWorldBot.create(3), command.name);
greeter.tell(new HelloWorld.Greet(command.name, replyTo));
return this;
}
}
要在生成actor时指定dispatcher,请使用DispatcherSelector。如果未指定,则参与者将使用默认分派器,有关详细信息,请参阅default分派器。
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {
// Start message...
public static Behavior<SayHello> create() {
return Behaviors.setup(HelloWorldMain::new);
}
private final ActorRef<HelloWorld.Greet> greeter;
private HelloWorldMain(ActorContext<SayHello> context) {
super(context);
final String dispatcherPath = "akka.actor.default-blocking-io-dispatcher";
Props greeterProps = DispatcherSelector.fromConfig(dispatcherPath);
greeter = getContext().spawn(HelloWorld.create(), "greeter", greeterProps);
}
// createReceive ...
}
请参考actor了解上述示例的大致内容。
SpawnProtocol
监护actor应该负责任务的初始化,并创建应用程序的初始actor,但有时您可能希望从监护参与者的外部派生新的参与者。例如,为每个HTTP请求创建一个actor。
这在您的行为中并不难实现,但是由于这是一种常见的模式,因此有一个预定义的消息协议和行为实现。它可以作为行为人系统的监护行为人,也可以与行为相结合。设置以启动一些初始任务或actor。然后可以通过告诉或询问SpawnProtocol从外部启动子actor。衍生到系统的actor引用。使用ask类似于如何ActorSystem。actorOf可以在经典的actor中使用,区别在于返回ActorRef的CompletionStage。
监护人行为可以定义为:
import akka.actor.typed.Behavior;
import akka.actor.typed.SpawnProtocol;
import akka.actor.typed.javadsl.Behaviors;
public abstract class HelloWorldMain {
private HelloWorldMain() {}
public static Behavior<SpawnProtocol.Command> create() {
return Behaviors.setup(
context -> {
// Start initial tasks
// context.spawn(...)
return SpawnProtocol.create();
});
}
}
和ActorSystem 可以创建与main为和要求衍生其他actor:
import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Props;
import akka.actor.typed.javadsl.AskPattern;
final ActorSystem<SpawnProtocol.Command> system =
ActorSystem.create(HelloWorldMain.create(), "hello");
final Duration timeout = Duration.ofSeconds(3);
CompletionStage<ActorRef<HelloWorld.Greet>> greeter =
AskPattern.ask(
system,
replyTo ->
new SpawnProtocol.Spawn<>(HelloWorld.create(), "greeter", Props.empty(), replyTo),
timeout,
system.scheduler());
Behavior<HelloWorld.Greeted> greetedBehavior =
Behaviors.receive(
(context, message) -> {
context.getLog().info("Greeting for {} from {}", message.whom, message.from);
return Behaviors.stopped();
});
CompletionStage<ActorRef<HelloWorld.Greeted>> greetedReplyTo =
AskPattern.ask(
system,
replyTo -> new SpawnProtocol.Spawn<>(greetedBehavior, "", Props.empty(), replyTo),
timeout,
system.scheduler());
greeter.whenComplete(
(greeterRef, exc) -> {
if (exc == null) {
greetedReplyTo.whenComplete(
(greetedReplyToRef, exc2) -> {
if (exc2 == null) {
greeterRef.tell(new HelloWorld.Greet("Akka", greetedReplyToRef));
}
});
}
});
SpawnProtocol也可以在actor层次结构的其他地方使用。它不必是根守护角色。
在Actor发现中描述了查找运行Actor的方法。
停止actor
一个actor可以通过返回行为来停止自己。停止作为下一个行为。
通过使用父actor的ActorContext的stop方法,可以强制子actor在处理完当 前消息后停止。只有童星可以用这种方式停止。
所有的子actor将在其父actor被停止时停止。
当一个actor停止时,它会接收到一个PostStop信号,这个信号可以用来清理资源。一个回调函数可以被指定为行为的参数。在优雅停止时处理PostStop信号。这允许在突然停止时应用不同的操作。
下面是一个例子:
import java.util.concurrent.TimeUnit;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
import akka.actor.typed.PostStop;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;
public class MasterControlProgram extends AbstractBehavior<MasterControlProgram.Command> {
interface Command {}
public static final class SpawnJob implements Command {
public final String name;
public SpawnJob(String name) {
this.name = name;
}
}
public enum GracefulShutdown implements Command {
INSTANCE
}
public static Behavior<Command> create() {
return Behaviors.setup(MasterControlProgram::new);
}
public MasterControlProgram(ActorContext<Command> context) {
super(context);
}
@Override
public Receive<Command> createReceive() {
return newReceiveBuilder()
.onMessage(SpawnJob.class, this::onSpawnJob)
.onMessage(GracefulShutdown.class, message -> onGracefulShutdown())
.onSignal(PostStop.class, signal -> onPostStop())
.build();
}
private Behavior<Command> onSpawnJob(SpawnJob message) {
getContext().getSystem().log().info("Spawning job {}!", message.name);
getContext().spawn(Job.create(message.name), message.name);
return this;
}
private Behavior<Command> onGracefulShutdown() {
getContext().getSystem().log().info("Initiating graceful shutdown...");
// perform graceful stop, executing cleanup before final system termination
// behavior executing cleanup is passed as a parameter to Actor.stopped
return Behaviors.stopped(() -> getContext().getSystem().log().info("Cleanup!"));
}
private Behavior<Command> onPostStop() {
getContext().getSystem().log().info("Master Control Program stopped");
return this;
}
}
public class Job extends AbstractBehavior<Job.Command> {
interface Command {}
public static Behavior<Command> create(String name) {
return Behaviors.setup(context -> new Job(context, name));
}
private final String name;
public Job(ActorContext<Command> context, String name) {
super(context);
this.name = name;
}
@Override
public Receive<Job.Command> createReceive() {
return newReceiveBuilder().onSignal(PostStop.class, postStop -> onPostStop()).build();
}
private Behavior<Command> onPostStop() {
getContext().getSystem().log().info("Worker {} stopped", name);
return this;
}
}
final ActorSystem<MasterControlProgram.Command> system =
ActorSystem.create(MasterControlProgram.create(), "B6700");
system.tell(new MasterControlProgram.SpawnJob("a"));
system.tell(new MasterControlProgram.SpawnJob("b"));
// sleep here to allow time for the new actors to be started
Thread.sleep(100);
system.tell(MasterControlProgram.GracefulShutdown.INSTANCE);
system.getWhenTerminated().toCompletableFuture().get(3, TimeUnit.SECONDS);
在从PostStop清理资源时,您还应该考虑对PreRestart信号执行相同的操作,该信号是在actor重新启动时发出的。注意,PostStop不是为重新启动而发出的。
监听actor
为了在另一个actor终止时得到通知(即永久停止,而不是临时失败并重新启动),一个actor可以监视另一个actor。被观察actor的终止(见停止actor)时,将收到终止信号。
public class MasterControlProgram extends AbstractBehavior<MasterControlProgram.Command> {
interface Command {}
public static final class SpawnJob implements Command {
public final String name;
public SpawnJob(String name) {
this.name = name;
}
}
public static Behavior<Command> create() {
return Behaviors.setup(MasterControlProgram::new);
}
public MasterControlProgram(ActorContext<Command> context) {
super(context);
}
@Override
public Receive<Command> createReceive() {
return newReceiveBuilder()
.onMessage(SpawnJob.class, this::onSpawnJob)
.onSignal(Terminated.class, this::onTerminated)
.build();
}
private Behavior<Command> onSpawnJob(SpawnJob message) {
getContext().getSystem().log().info("Spawning job {}!", message.name);
ActorRef<Job.Command> job = getContext().spawn(Job.create(message.name), message.name);
getContext().watch(job);
return this;
}
private Behavior<Command> onTerminated(Terminated terminated) {
getContext().getSystem().log().info("Job stopped: {}", terminated.getRef().path().name());
return this;
}
}
监视的另一种方法是watchWith,它允许指定自定义消息而不是终止消息。这通常比使用watch和终止信号更可取,因为可以在消息中包含其他信息,以便稍后接收时使用。
与上面的示例类似,但是在任务完成时使用watchWith并对原始请求者进行应答。
public class MasterControlProgram extends AbstractBehavior<MasterControlProgram.Command> {
interface Command {}
public static final class SpawnJob implements Command {
public final String name;
public final ActorRef<JobDone> replyToWhenDone;
public SpawnJob(String name, ActorRef<JobDone> replyToWhenDone) {
this.name = name;
this.replyToWhenDone = replyToWhenDone;
}
}
public static final class JobDone {
public final String name;
public JobDone(String name) {
this.name = name;
}
}
private static final class JobTerminated implements Command {
final String name;
final ActorRef<JobDone> replyToWhenDone;
JobTerminated(String name, ActorRef<JobDone> replyToWhenDone) {
this.name = name;
this.replyToWhenDone = replyToWhenDone;
}
}
public static Behavior<Command> create() {
return Behaviors.setup(MasterControlProgram::new);
}
public MasterControlProgram(ActorContext<Command> context) {
super(context);
}
@Override
public Receive<Command> createReceive() {
return newReceiveBuilder()
.onMessage(SpawnJob.class, this::onSpawnJob)
.onMessage(JobTerminated.class, this::onJobTerminated)
.build();
}
private Behavior<Command> onSpawnJob(SpawnJob message) {
getContext().getSystem().log().info("Spawning job {}!", message.name);
ActorRef<Job.Command> job = getContext().spawn(Job.create(message.name), message.name);
getContext().watchWith(job, new JobTerminated(message.name, message.replyToWhenDone));
return this;
}
private Behavior<Command> onJobTerminated(JobTerminated terminated) {
getContext().getSystem().log().info("Job stopped: {}", terminated.name);
terminated.replyToWhenDone.tell(new JobDone(terminated.name));
return this;
}
}
请注意replyToWhenDone是如何包含在watchWith消息中,然后在稍后接收终止作业的消息时使用的。
被观察的actor可以是任何ActorRef,它不必是上面示例中的子actor。
应该注意,终止消息的生成与注册和终止发生的顺序无关。特别是,即使被监视的actor在注册时已经被终止,监视的actor也会收到终止消息。
多次注册并不一定导致产生多条消息,但没有保证只有一个收到这样的消息:如果终止观看actor的生成和消息排队,和另一个注册这个消息被处理之前完成,然后第二个消息将被排队,因为注册的监控已经终止的actor会导致立即终止消息的一代。
它也可以取消观看另一个actor的动态使用背景。unwatch(目标)。即使终止的消息已经在邮箱中排队,这也可以工作;在调用unwatch后,不会再处理该actor的终止消息。
当被监视的actor位于已从集群中删除的节点上时,也会发送终止的消息。