消息收件箱(Inbox)
Akka框架为我们准备了一个名叫"收件箱"的组件,使用它可以很方便的对Actor进行消息发送和接收,大大方便了应用程序与Actor之间的交互。
public class MyWorker extends UntypedAbstractActor {
private final LoggingAdapter loggingAdapter= Logging.getLogger(getContext().system(),this);
public static enum Msg{
WORKING,DONE,CLOSE;
}
@Override
public void onReceive(Object message) throws Throwable {
if(message ==Msg.WORKING){
loggingAdapter.info("I am working");
}else if (message==Msg.CLOSE){
loggingAdapter.info("I will shutdown");
getSender().tell(Msg.CLOSE,getSelf());
getContext().stop(getSelf());
}else{
unhandled(message);
}
}
public static void main(String[] args) {
ActorSystem system=ActorSystem.create("inboxdemo");
ActorRef a=system.actorOf(Props.create(MyWorker.class), "worker");
final Inbox inbox=Inbox.create(system);
inbox.watch(worker);
inbox.send(worler, Msg.WORKING);
inbox.send(worler, Msg.DONE);
inbox.send(worler, Msg.CLOSE);
while (true){
Object msg=inbox.receive(Duration.create(1, TimeUnit.SECONDS));
if(msg==Msg.CLOSE){
System.out.println("My Worker is Closing");
}else if(msg instanceof Terminated){
System.out.println("My worker is dead");
system.shutdown();
break;
}else
System.out.println(msg);
}
}
}
根据ActorSystem构造一个与之绑定的邮箱Inbox。接着使用邮箱监视MyWorker,这样就能在MyWorker停止后得到一个消息通知。向MyWorker发送消息。
消息路由
Akka提供了非常灵活的消息发送机制。有时候,我们也许会使用一组Actor而不是一个Actor来提供一项服务。这一组Actor中所有的Actor都是对等的,也就是说你可以找任何一个Actor来为你服务。这种情况下,如果快速有效地找到合适的Actor呢?为了解决这个问题,Akka使用一个路由器组件来封装消息的调度。系统提供了几种实用的消息路由策略,比如,轮询选择Actor进行消息发送,随机消息发送,将消息发送给最空间的Actor,甚至在组内广播消息。
public class WatchActor extends UntypedAbstractActor {
private final LoggingAdapter log= Logging.getLogger(getContext().system(),this);
public Router router;//定义路由器组件
{
List<Routee> routees=new ArrayList<>();
for (int i = 0; i < 5; i++) {
ActorRef worker=getContext().actorOf(Props.create(MyWorker.class),"worker_"+i);
getContext().watch(worker);
routees.add(new ActorRefRoutee(worker));
}
router=new Router(new RoundRobinRoutingLogic(),routees);//指定
}
@Override
public void onReceive(Object message) throws Throwable {
if(message instanceof MyWorker.Msg){
router.route(message,getSender());
}else if(message instanceof Terminated) {
router = router.removeRoutee(((Terminated) message).actor());
System.out.println(((Terminated) message).actor().path() + "is closed,routtes=" + router.routees().size());
if (router.routees().size() == 0) {
System.out.println("Close system");
RouteMain.flag.send(false);
getContext().system().shutdown;
}
}else {
unhandled(message);
}
}
public static Anget<Boolean> flag=Agent.create(true, ExecutionContexts.global());
public static void main(String[] args) {
ActorSystem system=ActorSystem.create("route");
ActorRef a=system.actorOf(Props.create(WatchActor.class),"watcher");
int i=1;
while (flag.get()){
a.tell(MyWorker.Msg.WORKONG,ActorRef.noSender());
if(i%10==0) a.tell(MyWorker.Msg.CLOSE,ActorRef.noSender());
i++;
}
}
}
内置状态转换
在很多场景下,Actor的业务逻辑可能比较复杂,Actor可能需要根据不同的状态对同一条消息做出不同的处理。一个Actor内部消息处理函数可以拥有多个不同的状态,在特定的状态下,可以对同一消息进行不同的处理,状态之间也可以任意切换。
模拟一个场景:给婴儿Actor发送睡觉和玩两种指令。如果婴儿正在生气,你让他睡觉,他就会说:“我已经生气了”,如果你让他去玩,他就会变得开心。如果他玩的高兴,你让他继续玩,他就会说:“我很愉快”,如果让他睡觉,他会变生气。
public class BabyActor extends UntypedAbstractActor {
private final LoggingAdapter log= Logging.getLogger(getContext().system(),this);
public static enum Msg{
SLEEP,PLAY,CLOSE;
}
Procedure<Object> angry=new Procedure<Object>() {
@Override
public void apply(Object param) throws Exception {
System.out.println("angryApply:"+param);
if(param==Msg.SLEEP){
getSender().tell("I am already angry",getSelf());
System.out.println("I am already angry");
}else if(param ==Msg.PLAY){
System.out.println("I like play");
getContext().become( happy);
}
}
};
Procedure<Object> happy=new Procedure<Object>() {
@Override
public void apply(Object param) throws Exception {
System.out.println("happyApply"+param);
if(param==Msg.PLAY){
getSender().tell("I am already happy:",getSender());
System.out.println("I am already happy");
}else if(param==Msg.SLEEP){
System.out.println("I don't want to sleep");
getContext().become((PartialFunction<Object, BoxedUnit>) angry);
}
}
};
@Override
public void onReceive(Object message) throws Throwable {
System.out.println("OnReceive"+message);
if(message==Msg.SLEEP){
getContext().become((PartialFunction<Object, BoxedUnit>) angry);
}else if(message==Msg.PLAY){
getContext().become((PartialFunction<Object, BoxedUnit>) happy);
}else {
unhandled(message);
}
}
public static void main(String[] args) {
ActorSystem system=ActorSystem.create("become");
ActorRef a=system.actorOf(Props.create(BabyActor.class),"babyActor");
system.actorOf(Props.create(WatchActor.class,a),"wathcer");
a.tell(Msg.PLAY,ActorRef.noSender());
a.tell(Msg.SLEEP,ActorRef.noSender());
a.tell(Msg.PLAY,ActorRef.noSender());
a.tell(Msg.PLAY,ActorRef.noSender());
a.tell(PoisonPill.getInstance(),ActorRef.noSender());
}
}