2011-12-15 18:19:40 +01:00
|
|
|
|
|
|
|
|
.. _routing-scala:
|
|
|
|
|
|
2011-05-06 10:09:16 +02:00
|
|
|
Routing (Scala)
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
.. sidebar:: Contents
|
|
|
|
|
|
|
|
|
|
.. contents:: :local:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
Akka-core includes some building blocks to build more complex message flow handlers, they are listed and explained below:
|
|
|
|
|
|
2011-05-20 12:29:48 +02:00
|
|
|
Router
|
2011-12-15 15:28:21 +01:00
|
|
|
------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-05-20 12:29:48 +02:00
|
|
|
A Router is an actor that routes incoming messages to outbound actors.
|
2011-12-15 15:28:21 +01:00
|
|
|
The router routes the messages sent to it to its underlying actors called 'routees'.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
Akka comes with four defined routers out of the box, but as you will see in this chapter it
|
|
|
|
|
is really easy to create your own. The four routers shipped with Akka are:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2012-01-05 17:59:19 +01:00
|
|
|
* ``akka.routing.RoundRobinRouter``
|
|
|
|
|
* ``akka.routing.RandomRouter``
|
|
|
|
|
* ``akka.routing.BroadcastRouter``
|
|
|
|
|
* ``akka.routing.ScatterGatherFirstCompletedRouter``
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2012-01-05 17:59:19 +01:00
|
|
|
Routers Explained
|
|
|
|
|
^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
This is an example of how to create a router that is defined in configuration:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#config
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#configurableRouting
|
|
|
|
|
|
|
|
|
|
This is an example of how to programatically create a router and set the number of routees it should create:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingNrOfInstances
|
|
|
|
|
|
|
|
|
|
You can also give the router already created routees as in:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingRoutees
|
|
|
|
|
|
|
|
|
|
When you create a router programatically you define the number of routees *or* you pass already created routees to it.
|
|
|
|
|
If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded.
|
|
|
|
|
|
|
|
|
|
*It is also worth pointing out that if you define the number of routees in the configuration file then this
|
|
|
|
|
value will be used instead of any programmatically sent parameters.*
|
|
|
|
|
|
|
|
|
|
Once you have the router actor it is just to send messages to it as you would to any actor:
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
router ! MyMsg
|
|
|
|
|
|
|
|
|
|
The router will apply its behavior to the message it receives and forward it to the routees.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
Router usage
|
|
|
|
|
^^^^^^^^^^^^
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
In this section we will describe how to use the different router types.
|
|
|
|
|
First we need to create some actors that will be used in the examples:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#printlnActor
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
and
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#fibonacciActor
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
RoundRobinRouter
|
|
|
|
|
****************
|
|
|
|
|
Routes in a `round-robin <http://en.wikipedia.org/wiki/Round-robin>`_ fashion to its routees.
|
|
|
|
|
Code example:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#roundRobinRouter
|
|
|
|
|
|
|
|
|
|
When run you should see a similar output to this:
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 18:19:40 +01:00
|
|
|
Received message '1' in actor $b
|
|
|
|
|
Received message '2' in actor $c
|
|
|
|
|
Received message '3' in actor $d
|
|
|
|
|
Received message '6' in actor $b
|
|
|
|
|
Received message '4' in actor $e
|
|
|
|
|
Received message '8' in actor $d
|
|
|
|
|
Received message '5' in actor $f
|
|
|
|
|
Received message '9' in actor $e
|
|
|
|
|
Received message '10' in actor $f
|
|
|
|
|
Received message '7' in actor $c
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
If you look closely to the output you can see that each of the routees received two messages which
|
|
|
|
|
is exactly what you would expect from a round-robin router to happen.
|
2011-12-15 18:19:40 +01:00
|
|
|
(The name of an actor is automatically created in the format ``$letter`` unless you specify it -
|
|
|
|
|
hence the names printed above.)
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
RandomRouter
|
|
|
|
|
************
|
|
|
|
|
As the name implies this router type selects one of its routees randomly and forwards
|
|
|
|
|
the message it receives to this routee.
|
|
|
|
|
This procedure will happen each time it receives a message.
|
|
|
|
|
Code example:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#randomRouter
|
|
|
|
|
|
|
|
|
|
When run you should see a similar output to this:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
2011-12-15 18:19:40 +01:00
|
|
|
Received message '1' in actor $e
|
|
|
|
|
Received message '2' in actor $c
|
|
|
|
|
Received message '4' in actor $b
|
|
|
|
|
Received message '5' in actor $d
|
|
|
|
|
Received message '3' in actor $e
|
|
|
|
|
Received message '6' in actor $c
|
|
|
|
|
Received message '7' in actor $d
|
|
|
|
|
Received message '8' in actor $e
|
|
|
|
|
Received message '9' in actor $d
|
|
|
|
|
Received message '10' in actor $d
|
2011-12-15 15:28:21 +01:00
|
|
|
|
|
|
|
|
The result from running the random router should be different, or at least random, every time you run it.
|
|
|
|
|
Try to run it a couple of times to verify its behavior if you don't trust us.
|
|
|
|
|
|
|
|
|
|
BroadcastRouter
|
|
|
|
|
***************
|
|
|
|
|
A broadcast router forwards the message it receives to *all* its routees.
|
|
|
|
|
Code example:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#broadcastRouter
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
When run you should see a similar output to this:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. code-block:: scala
|
|
|
|
|
|
2011-12-15 18:19:40 +01:00
|
|
|
Received message 'this is a broadcast message' in actor $f
|
|
|
|
|
Received message 'this is a broadcast message' in actor $d
|
|
|
|
|
Received message 'this is a broadcast message' in actor $e
|
|
|
|
|
Received message 'this is a broadcast message' in actor $c
|
|
|
|
|
Received message 'this is a broadcast message' in actor $b
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
As you can see here above each of the routees, five in total, received the broadcast message.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
ScatterGatherFirstCompletedRouter
|
|
|
|
|
*********************************
|
|
|
|
|
The ScatterGatherFirstCompletedRouter will send the message on to all its routees as a future.
|
|
|
|
|
It then waits for first result it gets back. This result will be sent back to original sender.
|
|
|
|
|
Code example:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
.. includecode:: code/akka/docs/routing/RouterTypeExample.scala#scatterGatherFirstCompletedRouter
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
When run you should see this:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
2011-12-15 18:19:40 +01:00
|
|
|
The result of calculating Fibonacci for 10 is 55
|
2011-12-15 15:28:21 +01:00
|
|
|
|
|
|
|
|
From the output above you can't really see that all the routees performed the calculation, but they did!
|
|
|
|
|
The result you see is from the first routee that returned its calculation to the router.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
Broadcast Messages
|
|
|
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
There is a special type of message that will be sent to all routees regardless of the router.
|
2011-12-15 19:04:57 +01:00
|
|
|
This message is called ``Broadcast`` and is used in the following manner:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
2011-12-15 18:19:40 +01:00
|
|
|
router ! Broadcast("Watch out for Davy Jones' locker")
|
2011-12-15 15:28:21 +01:00
|
|
|
|
|
|
|
|
Only the actual message is forwarded to the routees, i.e. "Watch out for Davy Jones' locker" in the example above.
|
|
|
|
|
It is up to the routee implementation whether to handle the broadcast message or not.
|
|
|
|
|
|
2012-01-10 15:53:27 +01:00
|
|
|
Dynamically Resizable Routers
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
All routers can be used with a fixed number of routees or with a resize strategy to adjust the number
|
|
|
|
|
of routees dynamically.
|
|
|
|
|
|
|
|
|
|
This is an example of how to create a resizable router that is defined in configuration:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#config-resize
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#configurableRoutingWithResizer
|
|
|
|
|
|
|
|
|
|
Several more configuration options are availble and described in ``akka.actor.deployment.default.resizer``
|
|
|
|
|
section of the reference :ref:`configuration`.
|
|
|
|
|
|
|
|
|
|
This is an example of how to programatically create a resizable router:
|
|
|
|
|
|
|
|
|
|
.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingWithResizer
|
|
|
|
|
|
|
|
|
|
|
2011-12-15 15:28:21 +01:00
|
|
|
Custom Router
|
|
|
|
|
^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
You can also create your own router should you not find any of the ones provided by Akka sufficient for your needs.
|
|
|
|
|
In order to roll your own router you have to fulfill certain criteria which are explained in this section.
|
|
|
|
|
|
|
|
|
|
The router created in this example is a simple vote counter. It will route the votes to specific vote counter actors.
|
|
|
|
|
In this case we only have two parties the Republicans and the Democrats. We would like a router that forwards all
|
|
|
|
|
democrat related messages to the Democrat actor and all republican related messages to the Republican actor.
|
|
|
|
|
|
|
|
|
|
We begin with defining the class:
|
|
|
|
|
|
|
|
|
|
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRouter
|
|
|
|
|
:exclude: crRoute
|
|
|
|
|
|
2012-01-05 17:59:19 +01:00
|
|
|
The next step is to implement the ``createRoute`` method in the class just defined:
|
2011-12-15 15:28:21 +01:00
|
|
|
|
|
|
|
|
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRoute
|
|
|
|
|
|
|
|
|
|
As you can see above we start off by creating the routees and put them in a collection.
|
|
|
|
|
|
|
|
|
|
Make sure that you don't miss to implement the line below as it is *really* important.
|
|
|
|
|
It registers the routees internally and failing to call this method will
|
|
|
|
|
cause a ``ActorInitializationException`` to be thrown when the router is used.
|
|
|
|
|
Therefore always make sure to do the following in your custom router:
|
|
|
|
|
|
|
|
|
|
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRegisterRoutees
|
|
|
|
|
|
|
|
|
|
The routing logic is where your magic sauce is applied. In our example it inspects the message types
|
|
|
|
|
and forwards to the correct routee based on this:
|
|
|
|
|
|
|
|
|
|
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRoutingLogic
|
|
|
|
|
|
|
|
|
|
As you can see above what's returned in the partial function is a ``List`` of ``Destination(sender, routee)``.
|
|
|
|
|
The sender is what "parent" the routee should see - changing this could be useful if you for example want
|
|
|
|
|
another actor than the original sender to intermediate the result of the routee (if there is a result).
|
|
|
|
|
For more information about how to alter the original sender we refer to the source code of
|
2012-01-05 17:59:19 +01:00
|
|
|
`ScatterGatherFirstCompletedRouter <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L375>`_
|
2011-12-15 15:28:21 +01:00
|
|
|
|
|
|
|
|
All in all the custom router looks like this:
|
|
|
|
|
|
|
|
|
|
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#CustomRouter
|
|
|
|
|
|
|
|
|
|
If you are interested in how to use the VoteCountRouter you can have a look at the test class
|
|
|
|
|
`RoutingSpec <https://github.com/jboner/akka/blob/master/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala>`_
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2012-01-10 15:53:27 +01:00
|
|
|
Custom Resizer
|
|
|
|
|
**************
|
2011-04-09 19:55:46 -06:00
|
|
|
|
2012-01-10 15:53:27 +01:00
|
|
|
A router with dynamically resizable number of routees is implemented by providing a ``akka.routing.Resizer``
|
|
|
|
|
in ``resizer`` method of the ``RouterConfig``. See ``akka.routing.DefaultResizer`` for inspiration
|
|
|
|
|
of how to write your own resize strategy.
|
2011-04-09 19:55:46 -06:00
|
|
|
|