什么是Router
akka可以将消息发送到一个路由器(Router)中,一个Router会包含一个或多个Routee(一个Routee可以理解为一个actor), 并且通过Router的分发策略分配到不同的Routee中去执行.
示例:
Master.java
public class Master extends UntypedActor {
private LoggingAdapter log = Logging.getLogger(this.context().system(), this);
Router router;
{
List<Routee> routees = new ArrayList<Routee>();
for (int i=0; i<5; i++) {
ActorRef r = getContext().actorOf(Props.create(Worker.class));
getContext().watch(r);
routees.add(new ActorRefRoutee(r));
}
router = new Router(new RoundRobinRoutingLogic(), routees);
}
public void onReceive(Object msg) throws Throwable {
log.info("master msg: " + msg);
if (msg instanceof Work) {
router.route(msg, getSender());
}
else if (msg instanceof Terminated) {
router = router.removeRoutee( ((Terminated) msg).actor() );
ActorRef r = getContext().actorOf(Props.create(Worker.class));
getContext().watch(r);
router = router.addRoutee(new ActorRefRoutee(r));
}
}
}
Work.java
public class Work implements Serializable {
private static final long serialVersionUID = 1L;
public final String payload;
public Work(String payload) {
this.payload = payload;
}
}
RouterMain.java
public class RouterMain {
public static void main(String[] args) {
ActorSystem actorSystem = ActorSystem.create();
ActorRef actor = actorSystem.actorOf(Props.create(Master.class), "master");
actor.tell(new Work("111"), ActorRef.noSender());
actor.tell(new Work("222"), ActorRef.noSender());
actor.tell(new Work("333"), ActorRef.noSender());
actor.tell(new Work("444"), ActorRef.noSender());
actor.tell(new Work("555"), ActorRef.noSender());
actor.tell(new Work("666"), ActorRef.noSender());
actor.tell(new Work("777"), ActorRef.noSender());
}
}
Worker.java
public class Worker extends UntypedActor {
private LoggingAdapter log = Logging.getLogger(this.context().system(), this);
@Override
public void onReceive(Object o) throws Throwable {
log.info("Worker receive msg: " + o);
if (o instanceof Work) {
Work w = (Work) o;
this.getSender().tell("OK->" + w.payload, this.getSelf());
}
}
}
在这个例子中我们使用的是RoundRobinRoutingLogic(按顺序轮询)的策略来分发消息.
分发策略
- akka.routing.RoundRobinRoutingLogic
轮询策略, 按顺序将message发送给routee
- akka.routing.RandomRoutingLogic
随机策略
- akka.routing.SmallestMailboxRoutingLogic
邮箱最小策略, 将消息发送给邮箱中message数量最小的routee
- akka.routing.BroadcastRoutingLogic
广播策略, 所有的routee都会收到message
- akka.routing.ScatterGatherFistCompleteRoutingLogic
广播策略, 与BroadcastRoutingLogic不同的是这个策略会收到回复消息(一个message只有一条reply)
Routee
我们可以使用ActorRefRoutee来包装一个actor来实现一个Routee, 另外我们需要watch这个actor,目的是在这个actor销毁时可以在我们Router里去掉对应的Routee
Router Actor
我们可以使用示例1的方法, 通过自定义一个actor,然后新建一个Router对象并且指定对应的RoutingLogic来实现一个路由器功能. 也可以通过以下两个方式来创建路由器:
- Pool
通过Pool创建的routees会成为该Pool的child actor,并且routee会在它们销毁之前从router中自动的清理掉(即routeed已经被watch).
- Group
通过Group创建的router, 它的routees需要手动创建,消息是通过actor selection的方式分发到对应的routee中, 并且routee的actor也没有被watch.
通过配置文件创建Pool
akka.actor.deployment {
/parent/router1 {
router = round-robin-pool
nr-of-instances = 5
}
}
ActorRef router1 =
getContext().actorOf(FromConfig.getInstance().props(Props.create(Worker.class)),
"router1");
通过代码创建Pool
ActorRef router2 =
getContext().actorOf(new RoundRobinPool(5).props(Props.create(Worker.class)),
"router2");
通过配置文件创建Group
akka.actor.deployment {
/parent/router3 {
router = round-robin-group
routees.paths = ["/user/workers/w1", "/user/workers/w2", "/user/workers/w3"]
}
}
ActorRef router3 =
getContext().actorOf(FromConfig.getInstance().props(), "router3");
通过代码方式创建Group
ActorRef router4 =
getContext().actorOf(new RoundRobinGroup(paths).props(), "router4");
与Pool方式不同的是, Group中的routees需要自己创建
system.actorOf(Props.create(Workers.class), "workers");