AKKa支持在runtime时hotswapped (热插拔)Actor的message loop:在Actor中调用context.become方法。hotswapped的代码被存在一个栈中,可以被pushed(replacing 或 adding 在顶部)和popped。become一个特别好的例子是用它来实现一个有限状态机(FSM)。 Become/Unbecome特性很方便去实现有限状态转换机。
需要注意的是,当actor被它的Supervisor重新启动时,它将重置为原始行为。[1]
import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class RequestActor6 extends AbstractActor {
protected final String name;
protected final LoggingAdapter log = Logging.getLogger(context().system(), this);
private Receive hiHandler;
private Receive helloHandler;
public RequestActor6(String name) {
this.name = name;
hiHandler = receiveBuilder().matchEquals("Hi", message - >{
log.info(message);
getContext().become(helloHandler);
}).matchAny(message - >{
log.error(message.toString());
}).build();
helloHandler = receiveBuilder().matchEquals("Hello", message - >{
log.info(message);
getContext().become(hiHandler);
}).matchAny(message - >{
log.error(message.toString());
}).build();
}
public static Props props(String name) {
return Props.create(RequestActor6.class, name);
}
@Override public Receive createReceive() {
return receiveBuilder().match(String.class, message - >{
log.info(message);
getContext().become(helloHandler);
}).build();
}
}
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.testkit.TestActorRef;
import org.junit.jupiter.api.Test;
class RequestActor6Test {
ActorSystem system = ActorSystem.create();
@Test public void test() {
TestActorRef < RequestActor6 > ref = TestActorRef.create(system, RequestActor6.props("request6"));
ref.tell("init", ActorRef.noSender()); //info init
ref.tell("Hello", ActorRef.noSender()); //info Hello
ref.tell("Hi", ActorRef.noSender()); //info Hi
ref.tell("Hello", ActorRef.noSender()); //info Hello
ref.tell("Hi", ActorRef.noSender()); //info Hi
ref.tell("Hello", ActorRef.noSender()); //info Hello
ref.tell("Hello", ActorRef.noSender()); //error Hello
}
}
上面例子展示的是通过replace策略来替换栈头的handle,除此外还能通过pop(即 unbecom)策略移除当前handle切换上一个handle,在pop策略下需要保证"push"和“pop”操作次数要相等,否则会引起内存泄漏。
import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class RequestActorUnbecome extends AbstractActor {
protected final String name;
protected final LoggingAdapter log = Logging.getLogger(context().system(), this);
private AbstractActor.Receive hiHandler;
private AbstractActor.Receive helloHandler;
public RequestActorUnbecome(String name) {
this.name = name;
hiHandler = receiveBuilder().matchEquals("Hi", message - >{
log.info(message);
getContext().unbecome(); //切换到上一个handle
}).matchAny(message - >{
log.error(message.toString());
}).build();
helloHandler = receiveBuilder().matchEquals("Hello", message - >{
log.info(message);
getContext().become(hiHandler, false);//采用“pop”策略
}).matchAny(message - >{
log.error(message.toString());
}).build();
}
static Props props(String name) {
return Props.create(RequestActorUnbecome.class, name);
}
@Override public Receive createReceive() {
return receiveBuilder().matchEquals("init", message - >{
log.info(message);
getContext().become(helloHandler);
}).build();
}
}
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.testkit.TestActorRef;
import org.junit.jupiter.api.Test;
class RequestActorUnbecomeTest {
ActorSystem system = ActorSystem.create();
@Test
public void test() {
TestActorRef<RequestActorUnbecome> actorRef = TestActorRef.create(system,RequestActorUnbecome.props("Request"));
actorRef.tell("init", ActorRef.noSender());// info init
actorRef.tell("Hello",ActorRef.noSender());//info Hello
actorRef.tell("Hi",ActorRef.noSender());//info Hi
actorRef.tell("Hello",ActorRef.noSender());//info Hello
actorRef.tell("Hello",ActorRef.noSender());//error Hello
actorRef.tell("Hi",ActorRef.noSender());//info Hi
}