Akka提供了非常灵活的消息发送机制。有时候,会使用一组Actor而不是一个Actor来提供一项服务。这一组Actor中所有的Actor都是对等的,也就是说可以找任何一个Actor来服务。这种情况下,如何才能快速有效地找到合适的Actor?或者说如何调度这些消息,才可以使负载更为均衡地分配在这一组Actor中。
为了解决这个问题,Akka使用一个路由器组件(Router)来封装消息的调度。系统提供了几种实用的消息路由策略,比如,轮询选择Actor进行消息发送,随机消息发送,将消息发送给最为空闲的Actor,甚至是在组内广播消息。
public class WatchActor_Router extends UntypedActor {
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
List<Routee> routees = new ArrayList<Routee>();
public Router router;
{
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 msg) throws Exception {
if(msg instanceof MyWorker.Msg) {
router.route(msg,getSender());
} else if(msg instanceof Terminated) {
router = router.removeRoutee(((Terminated)msg).actor());
System.out.println(((Terminated)msg).actor().path() + " is closed, routees = " +
routees.size());
if(router.routees().size() == 0) {
System.out.println("Close system");
RouteMain.flag.send(false);
getContext().system().shutdown();
}
} else {
unhandled(msg);
}
}
}
上述代码定义了WatchActor。第3行,就是路由器组件Router,在构造Router时,需要指定路由策略和一组被路由的Actor(Routee),如第11行所示。这里使用了RoundRobinRoutingLogic路由策略,也就是对所有的Routee进行轮询消息发送。在本例中,Routee由5个MyWorker Actor构成(第6~10行,MyWorker与上一节中的相同)。
当有消息需要传递给这5个MyWorker时,只需要将消息投递给这个Router。Router就会根据给定的消息路由策略进行消息投递。当一个MyWorker停止工作时,还可以简单地将其从工作组中移除。在这里,如果发现系统中没有可用的Actor就会直接关闭系统。
主函数如下:
public class RouteMain {
public static Agent<Boolean> flag = Agent.create(true, ExecutionContexts.global());
public static void main(String[] args) throws InterruptedException {
ActorSystem system = ActorSystem.create("route", ConfigFactory.load("samplehello.conf"));
ActorRef w = system.actorOf(Props.create(WatchActor_Router.class), "watcher");
int i = 1;
while(flag.get()) {
w.tell(MyWorker.Msg.WORKING, ActorRef.noSender());
if(i%10==0)w.tell(MyWorker.Msg.CLOSE, ActorRef.noSender());
i++;
Thread.sleep(100);
}
}
}
上述代码向WatchActor发送大量消息,其中夹杂着几条关闭Actor的消息。这会使得MyWorker Actor逐一被关闭,最终程序将退出。
这段代码的部分输出如下(做过适当裁剪):
[INFO] [03/12/2017 21:43:44.361] [route-akka.actor.default-dispatcher-6] [akka://route/user/watcher/worker_0] I am working
[INFO] [03/12/2017 21:43:44.426] [route-akka.actor.default-dispatcher-6] [akka://route/user/watcher/worker_1] I am working
[INFO] [03/12/2017 21:43:44.526] [route-akka.actor.default-dispatcher-6] [akka://route/user/watcher/worker_2] I am working
[INFO] [03/12/2017 21:43:44.626] [route-akka.actor.default-dispatcher-7] [akka://route/user/watcher/worker_3] I am working
[INFO] [03/12/2017 21:43:44.726] [route-akka.actor.default-dispatcher-6] [akka://route/user/watcher/worker_4] I am working
[INFO] [03/12/2017 21:43:44.826] [route-akka.actor.default-dispatcher-3] [akka://route/user/watcher/worker_0] I am working
[INFO] [03/12/2017 21:43:44.926] [route-akka.actor.default-dispatcher-7] [akka://route/user/watcher/worker_1] I am working
[INFO] [03/12/2017 21:43:45.026] [route-akka.actor.default-dispatcher-3] [akka://route/user/watcher/worker_2] I am working
[INFO] [03/12/2017 21:43:45.126] [route-akka.actor.default-dispatcher-7] [akka://route/user/watcher/worker_3] I am working
[INFO] [03/12/2017 21:43:45.226] [route-akka.actor.default-dispatcher-6] [akka://route/user/watcher/worker_4] I am working
[INFO] [03/12/2017 21:43:45.226] [route-akka.actor.default-dispatcher-7] [akka://route/user/watcher/worker_0] I will shutdown
[INFO] [03/12/2017 21:43:45.258] [route-akka.actor.default-dispatcher-6]
MyWorker is stopping
[INFO] [03/12/2017 21:43:49.248] [route-akka.actor.default-dispatcher-3] [akka://route/user/watcher/worker_1] I will shutdown
[INFO] [03/12/2017 21:43:49.249] [route-akka.actor.default-dispatcher-3] [akka://route/deadLetters] Message [cn.guet.parallel.akka.MyWorker$Msg] from Actor[akka://route/user/watcher/worker_1#-1390948406] to Actor[akka://route/deadLetters] was not delivered. [5] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
akka://route/user/watcher/worker_1 is closed, routees = 5
Close system
可以看到,WORKING消息被轮流发送给这5个worker。可以修改路由策略,观测不同路由策略下的消息投递方式(除了RoundRobinRoutingLogic外,还可以尝试BroadcastBoutingLogic广播策略、RandomRoutingLogic随机投递策略、SmallestMailRoutingLogic空闲Actor优先投递策略)。