5. Routing messages
In the previous example, we created and used a single actor, and an actor can only process one message at the same time, which does not give play to the advantages of akka parallel computing. We hope to process messages in parallel, just like kafka's consumer. This requires the use of routing to akka.
Routing is the same as the conventional understanding. When a message arrives, it can be distributed to the actor s after routing according to a certain strategy. There are two types of routing in akka, pool routing and group routing.
- Pool Routing: from the name, a pile of actor s are in a pool.
- Group Routing: according to the name, a pile of actor s belong to a group.
What's the difference between the two?
In a non cluster environment or a single node environment, there is no difference between the two, and both can complete the load / distribution of messages** However: * * pool routing can only be routed within a single ActorSystem, while group routing can be routed across actorsystems within the cluster. (it can be understood that a pool has physical boundaries like a pool, while a group can cross regions like a group) as shown in the following figure.
- pool router:
- group router:
Moreover, the implementation methods of the two routes are also different.
pool router
Create a pool route as follows:
// Configuration of actor BehaviorConfig filterConfig = new BehaviorConfig("power > 2000"); // Create filter behavior Behavior<IDeviceMessage> filterBehavior = FilterActor.create(filterConfig, null); // Encapsulate filter behavior into pool router behavior Behavior<IDeviceMessage> filterPoolBehavior = Routers.pool(5, filterBehavior.narrow()).withRoundRobinRouting(); // Instantiate filter pool router ActorRef<IDeviceMessage> filterPoolRouterRef = context.spawn(filterPoolBehavior, "FilterPoolRouter"); DevicePropertyMessage message = DevicePropertyMessage.builder() .thingId("D007") .current(3.58) .voltage(380.00) .build(); // Send message to pool router for (int i = 0; i < 10; i++) { filterPoolRouterRef.tell(message); }
There are three main steps:
- First create the Behavior of the basic actor in the pool. Here, we create its pool actor for FilterActor.
- Encapsulate the Behavior of the basic actor into the Behavior of the pool actor through the routes. Pool () method. Here, you can configure the number of sub actors of each pool route and the routing policy (supporting polling, randomization, broadcasting and consistent hashing mechanisms).
- Instantiate the pool actor through the spawn() method.
In this way, when we send messages to the pool route, the pool route will poll and send messages to the five internal sub actor s according to the RoundRobin policy configured here.
In the above example, we send 10 messages to the pool router. There are five sub actors under the pool. When the FilterActor receives the message, we print out the actor path, thread id and objectId of the current actor in turn. The results are as follows:
=== actor system started === [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$b threadId=28 objectId=1644765223 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$c threadId=29 objectId=710798201 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$e threadId=31 objectId=1877431981 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$d threadId=30 objectId=1124465799 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$a threadId=23 objectId=610546090 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$c threadId=29 objectId=710798201 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$e threadId=31 objectId=1877431981 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$b threadId=28 objectId=1644765223 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$d threadId=30 objectId=1124465799 [filter actor] Message received: actor path=akka://local-example/user/FilterPoolRouter/$a threadId=23 objectId=610546090
The following points can be found:
- The child actor is represented as FilterPoolRouter/ a , F i l t e r P o o l R o u t e r / a, FilterPoolRouter/ a. FilterPoolRouter/b, where FilterPoolRouter is the name of pool actor. It can be seen that each child actor is at the next level of pool actor.
- Messages will be processed by different threads and actor classes (this involves akka's message dispatcher mechanism. If you are interested, please refer to the Dispatchers section of the official document)
group router
Create a group route as follows:
// Configuration of actor BehaviorConfig filterConfig = new BehaviorConfig("power > 2000"); // Create filter behavior Behavior<IDeviceMessage> filterBehavior = FilterActor.create(filterConfig, null); // Instantiate filter actor ActorRef<IDeviceMessage> filterActorRef = context.spawn(filterBehavior, "FilterActor"); // Register with a key in the filter Pool Router ServiceKey<IDeviceMessage> filterServiceKey = ServiceKey.create(IDeviceMessage.class, "FilterGroupRouterKey"); context.getSystem().receptionist().tell(Receptionist.register(filterServiceKey, filterActorRef.narrow()));
There are three main steps:
- Instantiate a common actor and obtain its ActorRef
- Create a service key for the group router, which is the unique ID of the group router
- Register the above actor to the key through receptionist().tell()
In this way, we have registered an actor to the group routing. How can I send messages to this group?
//Create a key with the same name ServiceKey<IDeviceMessage> filterServiceKey = ServiceKey.create(IDeviceMessage.class, "FilterGroupRouterKey"); //Get ActorRef of group route Behavior<IDeviceMessage> filterGroupBehavior = Routers.group(filterServiceKey).withRoundRobinRouting(); ActorRef<IDeviceMessage> filterGroupRouterRef = context.spawn(filterGroupBehavior, "FilterGroupRouter"); // Send message to group router filterGroupRouterRef.tell(message);
It can also be divided into three steps:
- First create a ServiceKey with the same name, or directly use the created ServiceKey
- Get the Behavior of the group router through the routes. Group() method, and specify the routing policy at the same time
- Use context.spawn() to create the actor of the group router, so you can get the ActorRef of the group router and send messages.
Similarly, we register five actors to the group router, and then send 10 messages to the group router to see the actor path, thread id and objectId. The results are as follows:
[filter actor] Message received: actor path=akka://local-example/user/FilterActor3 threadId=29 objectId=868900288 [filter actor] Message received: actor path=akka://local-example/user/FilterActor2 threadId=28 objectId=1697801780 [filter actor] Message received: actor path=akka://local-example/user/FilterActor4 threadId=30 objectId=1650033925 [filter actor] Message received: actor path=akka://local-example/user/FilterActor1 threadId=23 objectId=1977819714 [filter actor] Message received: actor path=akka://local-example/user/FilterActor5 threadId=31 objectId=403997215 [filter actor] Message received: actor path=akka://local-example/user/FilterActor3 threadId=29 objectId=868900288 [filter actor] Message received: actor path=akka://local-example/user/FilterActor4 threadId=30 objectId=1650033925 [filter actor] Message received: actor path=akka://local-example/user/FilterActor2 threadId=28 objectId=1697801780 [filter actor] Message received: actor path=akka://local-example/user/FilterActor1 threadId=23 objectId=1977819714 [filter actor] Message received: actor path=akka://local-example/user/FilterActor5 threadId=31 objectId=403997215
It can be found that:
- Messages are still distributed evenly across the five actor s, processed by different classes and threads
- It is worth noting that the actor path of the actor is different from the pool actor. Each actor is not below the level of the group actor, that is, they are not child actors of the group actor.
Here, we can learn the hierarchical logic of actors, as shown in the figure below. In the actor system, each actor is hierarchical, and this level is reflected in the actor path. All the actors we create through the spawn() method will be under user, and the child actors will be managed by the parent actor.
**Note: * * in the typed version, actor path is not very useful. In the classic version, we can obtain ActorRef through actor path.
To sum up, the logic of the pool router is the management mode, which tells it the Behavior (i.e. Behavior), routing rules and the number of sub actors to be routed. It will create a pile of sub actors to distribute messages. The logic of the group router is the registration mode. Give the router a key, then register the actor on the key, and then check the registry to realize message distribution. In addition, a very important difference is that the pool router can only be implemented under the current actor system, while the group router can implement routing within the cluster across the actor system. It will be further introduced later when implementing the cluster.