Merge pull request #698 from akka/wip-944-consistent-hashing-patriknw
Consistent hashing router, see #944
This commit is contained in:
commit
888f81df8d
19 changed files with 1236 additions and 309 deletions
|
|
@ -0,0 +1,5 @@
|
|||
package docs.jrouting;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class ConsistentHashingRouterDocTest extends ConsistentHashingRouterDocTestBase with JUnitSuite
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import akka.testkit.JavaTestKit;
|
||||
import akka.actor.ActorSystem;
|
||||
|
||||
//#imports1
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.routing.ConsistentHashingRouter.ConsistentHashable;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.io.Serializable;
|
||||
//#imports1
|
||||
|
||||
//#imports2
|
||||
import akka.actor.Props;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.routing.ConsistentHashingRouter;
|
||||
import akka.routing.ConsistentHashingRouter.ConsistentHashMapper;
|
||||
import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope;
|
||||
//#imports2
|
||||
|
||||
public class ConsistentHashingRouterDocTestBase {
|
||||
|
||||
static ActorSystem system;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
system = ActorSystem.create();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void teardown() {
|
||||
system.shutdown();
|
||||
}
|
||||
|
||||
//#cache-actor
|
||||
|
||||
public static class Cache extends UntypedActor {
|
||||
Map<String, String> cache = new HashMap<String, String>();
|
||||
|
||||
public void onReceive(Object msg) {
|
||||
if (msg instanceof Entry) {
|
||||
Entry entry = (Entry) msg;
|
||||
cache.put(entry.key, entry.value);
|
||||
} else if (msg instanceof Get) {
|
||||
Get get = (Get) msg;
|
||||
Object value = cache.get(get.key);
|
||||
getSender().tell(value == null ? NOT_FOUND : value,
|
||||
getContext().self());
|
||||
} else if (msg instanceof Evict) {
|
||||
Evict evict = (Evict) msg;
|
||||
cache.remove(evict.key);
|
||||
} else {
|
||||
unhandled(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Evict implements Serializable {
|
||||
public final String key;
|
||||
public Evict(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Get implements Serializable, ConsistentHashable {
|
||||
public final String key;
|
||||
public Get(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
public Object consistentHashKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Entry implements Serializable {
|
||||
public final String key;
|
||||
public final String value;
|
||||
public Entry(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String NOT_FOUND = "NOT_FOUND";
|
||||
//#cache-actor
|
||||
|
||||
|
||||
@Test
|
||||
public void demonstrateUsageOfConsistentHashableRouter() {
|
||||
|
||||
new JavaTestKit(system) {{
|
||||
|
||||
//#consistent-hashing-router
|
||||
|
||||
final ConsistentHashMapper hashMapper = new ConsistentHashMapper() {
|
||||
@Override
|
||||
public Object hashKey(Object message) {
|
||||
if (message instanceof Evict) {
|
||||
return ((Evict) message).key;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ActorRef cache = system.actorOf(new Props(Cache.class).withRouter(
|
||||
new ConsistentHashingRouter(10).withHashMapper(hashMapper)),
|
||||
"cache");
|
||||
|
||||
cache.tell(new ConsistentHashableEnvelope(
|
||||
new Entry("hello", "HELLO"), "hello"), getRef());
|
||||
cache.tell(new ConsistentHashableEnvelope(
|
||||
new Entry("hi", "HI"), "hi"), getRef());
|
||||
|
||||
cache.tell(new Get("hello"), getRef());
|
||||
expectMsgEquals("HELLO");
|
||||
|
||||
cache.tell(new Get("hi"), getRef());
|
||||
expectMsgEquals("HI");
|
||||
|
||||
cache.tell(new Evict("hi"), getRef());
|
||||
cache.tell(new Get("hi"), getRef());
|
||||
expectMsgEquals(NOT_FOUND);
|
||||
|
||||
//#consistent-hashing-router
|
||||
}};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,13 +15,14 @@ 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:: ../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 +178,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 +209,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 +228,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 +251,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 +272,51 @@ 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.
|
||||
|
||||
There is 3 ways to define what data to use for the consistent hash key.
|
||||
|
||||
* You can define ``withHashMapper`` of the router to map incoming
|
||||
messages to their consistent hash key. This makes the the decision
|
||||
transparent for the sender.
|
||||
|
||||
* The messages may implement ``akka.routing.ConsistentHashingRouter.ConsistentHashable``.
|
||||
The key is part of the message and it's convenient to define it together
|
||||
with the message definition.
|
||||
|
||||
* The messages can be be wrapped in a ``akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope``
|
||||
to define what data to use for the consistent hash key. The sender knows
|
||||
the key to use.
|
||||
|
||||
These ways to define the consistent hash key can be use together and at
|
||||
the same time for one router. The ``withHashMapper`` is tried first.
|
||||
|
||||
Code example:
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTestBase.java
|
||||
:include: imports1,cache-actor
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTestBase.java
|
||||
:include: imports2,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``. The ``Evict``
|
||||
message is handled by the ``withHashMapper``.
|
||||
|
||||
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 +338,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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue