Documentation of constent hashing router, see #944

* Document sample to illustrate usage
* Rewrote RouterViaConfig to real DocSpec
* Added configuration snippets for all router types
This commit is contained in:
Patrik Nordwall 2012-09-11 15:10:33 +02:00
parent f6dcee423b
commit ea78e6d805
6 changed files with 323 additions and 61 deletions

View file

@ -171,8 +171,6 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
}
expectMsgType[ActorKilledException]
//#supervision
val router2 = system.actorOf(Props.empty.withRouter(RoundRobinRouter(1).withSupervisorStrategy(escalator)))
router2 ! CurrentRoutees
EventFilter[ActorKilledException](occurrences = 2) intercept {

View file

@ -21,7 +21,7 @@ Routers In Action
This is an example of how to create a router that is defined in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigExample.scala#config
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
.. includecode:: code/docs/jrouting/RouterViaConfigExample.java#configurableRouting
@ -177,6 +177,10 @@ is exactly what you would expect from a round-robin router to happen.
(The name of an actor is automatically created in the format ``$letter`` unless you specify it -
hence the names printed above.)
This is an example of how to define a round-robin router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
RandomRouter
************
As the name implies this router type selects one of its routees randomly and forwards
@ -204,6 +208,10 @@ When run you should see a similar output to this:
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.
This is an example of how to define a random router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-random
SmallestMailboxRouter
*********************
A Router that tries to send to the non-suspended routee with fewest messages in mailbox.
@ -219,6 +227,10 @@ Code example:
.. includecode:: code/docs/jrouting/ParentActor.java#smallestMailboxRouter
This is an example of how to define a smallest-mailbox router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-smallest-mailbox
BroadcastRouter
***************
A broadcast router forwards the message it receives to *all* its routees.
@ -238,6 +250,10 @@ When run you should see a similar output to this:
As you can see here above each of the routees, five in total, received the broadcast message.
This is an example of how to define a broadcast router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-broadcast
ScatterGatherFirstCompletedRouter
*********************************
The ScatterGatherFirstCompletedRouter will send the message on to all its routees as a future.
@ -255,6 +271,36 @@ When run you should see this:
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.
This is an example of how to define a scatter-gather router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-scatter-gather
ConsistentHashingRouter
***********************
The ConsistentHashingRouter uses `consistent hashing <http://en.wikipedia.org/wiki/Consistent_hashing>`_
to select a connection based on the sent message. This
`article <http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html>`_ gives good
insight into how consistent hashing is implemented.
The messages sent to a ConsistentHashingRouter must implement
``akka.routing.ConsistentHashable`` or be wrapped in a ``akka.routing.ConsistentHashableEnvelope``
to define what data to use for the consistent hash key. If returning a
byte array or String it will be used as is, otherwise the configured
:ref:`serializer <serialization-scala>` will be applied to the returned data
to create a byte array that will be hashed.
Code example:
FIXME Java example of consistent routing
In the above example you see that the ``Get`` message implements ``ConsistentHashable`` itself,
while the ``Entry`` message is wrapped in a ``ConsistentHashableEnvelope``.
This is an example of how to define a consistent-hashing router in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-consistent-hashing
Broadcast Messages
^^^^^^^^^^^^^^^^^^
@ -276,7 +322,7 @@ of routees dynamically.
This is an example of how to create a resizable router that is defined in configuration:
.. includecode:: ../scala/code/docs/routing/RouterViaConfigExample.scala#config-resize
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-resize
.. includecode:: code/docs/jrouting/RouterViaConfigExample.java#configurableRoutingWithResizer

View file

@ -0,0 +1,60 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.routing
import akka.testkit.AkkaSpec
import akka.testkit.ImplicitSender
object ConsistentHashingRouterDocSpec {
//#cache-actor
import akka.actor.Actor
import akka.routing.ConsistentHashingRouter.ConsistentHashable
import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope
class Cache extends Actor {
var cache = Map.empty[String, String]
def receive = {
case Entry(key, value) cache += (key -> value)
case Get(key) sender ! cache.get(key)
}
}
case class Get(key: String) extends ConsistentHashable {
override def consistentHashKey: Any = key
}
case class Entry(key: String, value: String)
case class EntryEnvelope(entry: Entry) extends ConsistentHashableEnvelope {
override def consistentHashKey: Any = entry.key
override def message: Any = entry
}
//#cache-actor
}
class ConsistentHashingRouterDocSpec extends AkkaSpec with ImplicitSender {
import ConsistentHashingRouterDocSpec._
"demonstrate usage of ConsistentHashableRouter" in {
//#consistent-hashing-router
import akka.actor.Props
import akka.routing.ConsistentHashingRouter
val cache = system.actorOf(Props[Cache].withRouter(ConsistentHashingRouter(10)), "cache")
cache ! EntryEnvelope(Entry("hello", "HELLO"))
cache ! EntryEnvelope(Entry("hi", "HI"))
cache ! Get("hello")
cache ! Get("hi")
expectMsg(Some("HELLO"))
expectMsg(Some("HI"))
//#consistent-hashing-router
}
}

View file

@ -0,0 +1,157 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.routing
import akka.actor.{ Actor, Props, ActorSystem, ActorLogging }
import com.typesafe.config.ConfigFactory
import akka.routing.FromConfig
import akka.routing.ConsistentHashingRouter.ConsistentHashable
import akka.testkit.AkkaSpec
import akka.testkit.ImplicitSender
object RouterWithConfigDocSpec {
val config = ConfigFactory.parseString("""
//#config-round-robin
akka.actor.deployment {
/myrouter1 {
router = round-robin
nr-of-instances = 5
}
}
//#config-round-robin
//#config-resize
akka.actor.deployment {
/myrouter2 {
router = round-robin
resizer {
lower-bound = 2
upper-bound = 15
}
}
}
//#config-resize
//#config-random
akka.actor.deployment {
/myrouter3 {
router = random
nr-of-instances = 5
}
}
//#config-random
//#config-smallest-mailbox
akka.actor.deployment {
/myrouter4 {
router = smallest-mailbox
nr-of-instances = 5
}
}
//#config-smallest-mailbox
//#config-broadcast
akka.actor.deployment {
/myrouter5 {
router = broadcast
nr-of-instances = 5
}
}
//#config-broadcast
//#config-scatter-gather
akka.actor.deployment {
/myrouter6 {
router = scatter-gather
nr-of-instances = 5
within = 10 seconds
}
}
//#config-scatter-gather
//#config-consistent-hashing
akka.actor.deployment {
/myrouter7 {
router = consistent-hashing
nr-of-instances = 5
}
}
//#config-consistent-hashing
""")
case class Message(nbr: Int) extends ConsistentHashable {
override def consistentHashKey = nbr
}
class ExampleActor extends Actor with ActorLogging {
def receive = {
case Message(nbr)
log.debug("Received %s in router %s".format(nbr, self.path.name))
sender ! nbr
}
}
}
class RouterWithConfigDocSpec extends AkkaSpec(RouterWithConfigDocSpec.config) with ImplicitSender {
import RouterWithConfigDocSpec._
"demonstrate configured round-robin router" in {
//#configurableRouting
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter1")
//#configurableRouting
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
"demonstrate configured random router" in {
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter3")
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
"demonstrate configured smallest-mailbox router" in {
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter4")
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
"demonstrate configured broadcast router" in {
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter5")
1 to 10 foreach { i router ! Message(i) }
receiveN(5 * 10)
}
"demonstrate configured scatter-gather router" in {
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter6")
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
"demonstrate configured consistent-hashing router" in {
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter7")
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
"demonstrate configured round-robin router with resizer" in {
//#configurableRoutingWithResizer
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"myrouter2")
//#configurableRoutingWithResizer
1 to 10 foreach { i router ! Message(i) }
receiveN(10)
}
}

View file

@ -1,52 +0,0 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.routing
import akka.actor.{ Actor, Props, ActorSystem }
import com.typesafe.config.ConfigFactory
import akka.routing.FromConfig
case class Message(nbr: Int)
class ExampleActor extends Actor {
def receive = {
case Message(nbr) println("Received %s in router %s".format(nbr, self.path.name))
}
}
object RouterWithConfigExample extends App {
val config = ConfigFactory.parseString("""
//#config
akka.actor.deployment {
/router {
router = round-robin
nr-of-instances = 5
}
}
//#config
//#config-resize
akka.actor.deployment {
/router2 {
router = round-robin
resizer {
lower-bound = 2
upper-bound = 15
}
}
}
//#config-resize
""")
val system = ActorSystem("Example", config)
//#configurableRouting
val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"router")
//#configurableRouting
1 to 10 foreach { i router ! Message(i) }
//#configurableRoutingWithResizer
val router2 = system.actorOf(Props[ExampleActor].withRouter(FromConfig()),
"router2")
//#configurableRoutingWithResizer
1 to 10 foreach { i router2 ! Message(i) }
}

View file

@ -15,15 +15,16 @@ is really easy to create your own. The routers shipped with Akka are:
* ``akka.routing.SmallestMailboxRouter``
* ``akka.routing.BroadcastRouter``
* ``akka.routing.ScatterGatherFirstCompletedRouter``
* ``akka.routing.ConsistentHashingRouter``
Routers In Action
^^^^^^^^^^^^^^^^^
This is an example of how to create a router that is defined in configuration:
.. includecode:: code/docs/routing/RouterViaConfigExample.scala#config
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
.. includecode:: code/docs/routing/RouterViaConfigExample.scala#configurableRouting
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#configurableRouting
This is an example of how to programmatically create a router and set the number of routees it should create:
@ -125,7 +126,7 @@ not have an effect on the number of actors in the pool.
Setting the strategy is easily done:
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala
.. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#supervision
:include: supervision
:exclude: custom-strategy
@ -179,6 +180,10 @@ is exactly what you would expect from a round-robin router to happen.
(The name of an actor is automatically created in the format ``$letter`` unless you specify it -
hence the names printed above.)
This is an example of how to define a round-robin router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
RandomRouter
************
As the name implies this router type selects one of its routees randomly and forwards
@ -206,6 +211,10 @@ When run you should see a similar output to this:
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.
This is an example of how to define a random router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-random
SmallestMailboxRouter
*********************
A Router that tries to send to the non-suspended routee with fewest messages in mailbox.
@ -221,6 +230,11 @@ Code example:
.. includecode:: code/docs/routing/RouterTypeExample.scala#smallestMailboxRouter
This is an example of how to define a smallest-mailbox router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-smallest-mailbox
BroadcastRouter
***************
A broadcast router forwards the message it receives to *all* its routees.
@ -240,6 +254,11 @@ When run you should see a similar output to this:
As you can see here above each of the routees, five in total, received the broadcast message.
This is an example of how to define a broadcast router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-broadcast
ScatterGatherFirstCompletedRouter
*********************************
The ScatterGatherFirstCompletedRouter will send the message on to all its routees as a future.
@ -257,6 +276,40 @@ When run you should see this:
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.
This is an example of how to define a scatter-gather router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-scatter-gather
ConsistentHashingRouter
***********************
The ConsistentHashingRouter uses `consistent hashing <http://en.wikipedia.org/wiki/Consistent_hashing>`_
to select a connection based on the sent message. This
`article <http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html>`_ gives good
insight into how consistent hashing is implemented.
The messages sent to a ConsistentHashingRouter must implement
``akka.routing.ConsistentHashable`` or be wrapped in a ``akka.routing.ConsistentHashableEnvelope``
to define what data to use for the consistent hash key. If returning a
byte array or String it will be used as is, otherwise the configured
:ref:`serializer <serialization-scala>` will be applied to the returned data
to create a byte array that will be hashed.
Code example:
.. includecode:: code/docs/routing/ConsistentHashingRouterDocSpec.scala#cache-actor
.. includecode:: code/docs/routing/ConsistentHashingRouterDocSpec.scala#consistent-hashing-router
In the above example you see that the ``Get`` message implements ``ConsistentHashable`` itself,
while the ``Entry`` message is wrapped in a ``ConsistentHashableEnvelope``.
This is an example of how to define a consistent-hashing router in configuration:
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-consistent-hashing
Broadcast Messages
^^^^^^^^^^^^^^^^^^
@ -278,9 +331,9 @@ of routees dynamically.
This is an example of how to create a resizable router that is defined in configuration:
.. includecode:: code/docs/routing/RouterViaConfigExample.scala#config-resize
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#config-resize
.. includecode:: code/docs/routing/RouterViaConfigExample.scala#configurableRoutingWithResizer
.. includecode:: code/docs/routing/RouterViaConfigDocSpec.scala#configurableRoutingWithResizer
Several more configuration options are available and described in ``akka.actor.deployment.default.resizer``
section of the reference :ref:`configuration`.