流程
Stop 一个actor会有有两个步骤,首先actor会暂停其邮箱处理和发送stop command给所有children actors, 这一步骤会在stop children actors阶段持续,直到stop最后一个children actor才会进入下一个步骤,stop自己本身(调用postStop钩子函数,倾销mailbox,通知DeathWatch和supervisor)。 这两个步骤确保actor系统子树以有序的方式销毁,将停止(销毁)命令传播给叶子,并将它们的确认传回supervisor。如果其中一个actor没有响应(即处理消息的时间延长,因此没有收到停止命令),整个进程将被卡住。当完成stop 一个 actor后,会调用postStop()钩子函数,可以在postStop定义清理资源的动作。
需要注意的是,stop actor实际上是异步执行的,在发出stop actor 命令时actor不会立即被销毁,所以在真正销毁之前会持续处理现有的消息,但mailbox的消息不会被处理 (e. stop may return before the actor is stopped.),mailbox中未消费的消息会默认发送到ActorSystem的deadLetters(可通过定义mailbox的实现改变规则)。[1]
方法
1.通过调用ActorRefFactory的stop方法来停止Actor, 例如使用ActorContext或ActorSystem(ActorContext是继承了ActorRefFactory 和ActorSystem是实现ActorRefFactory),通常ActorContext用来stopactor自己及子actor,而ActorSystem用来stop top-level的actors.
2.通过向actor发送akka.actor.PoisonPill 消息,actor当消费到PoisonPill消息时开始执行stop流程。
3.通过向actor发送akka.actor.Kill消息,与PoisonPill不同的是,Kill actor会抛出ActorKilledException,好处就是触发exception时会去询问它的supervisor如何去处理(恢复,重启,停止)。
4.通过gracefulStop 方法stop actors,适用于设置等待终止时间和通过stop若干个actor的场景
实践
import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import java.util.HashMap;
import java.util.Map;
public class RequestActor5 extends AbstractActor {
protected final String name;
protected final LoggingAdapter log = Logging.getLogger(context().system(),this);
protected final Map<String, Object> map = new HashMap<>();
public RequestActor5(String name) {
this.name = name;
}
static Props props(String name) {
return Props.create(RequestActor5.class, name);
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("done",message -> {
getContext().stop(getSelf());
})
.match(String.class,message->{
log.info("Received Request {}",message);
map.put("StringMessage",message);
})
.matchAny(o->log.info("Received unknow message {}",o))
.build();
}
@Override
public void postStop() throws Exception {
System.out.println("RequestActor5 is done");
}
}
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Kill;
import akka.actor.PoisonPill;
import akka.testkit.TestActorRef;
import org.junit.jupiter.api.Test;
class RequestActor5Test {
ActorSystem system = ActorSystem.create();
@Test
public void test1() {
TestActorRef<RequestActor5> actorRef = TestActorRef.create(system, RequestActor5.props("request"));
actorRef.tell(Kill.getInstance(), ActorRef.noSender());
}
@Test
public void test2() {
TestActorRef<RequestActor5> actorRef = TestActorRef.create(system, RequestActor5.props("request"));
actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender());
}
@Test
public void test3() {
TestActorRef<RequestActor5> actorRef = TestActorRef.create(system, RequestActor5.props("request"));
actorRef.tell("done", ActorRef.noSender());
}
@Test
public void test4() {
TestActorRef<RequestActor5> actorRef = TestActorRef.create(system, RequestActor5.props("request"));
actorRef.tell("message", ActorRef.noSender());
system.stop(actorRef);
}
}