2013-01-14 14:09:53 +01:00
|
|
|
.. _cluster-singleton:
|
|
|
|
|
|
|
|
|
|
Cluster Singleton Pattern
|
|
|
|
|
=========================
|
|
|
|
|
|
|
|
|
|
For some use cases it is convenient and sometimes also mandatory to ensure that
|
|
|
|
|
you have exactly one actor of a certain type running somewhere in the cluster.
|
|
|
|
|
|
|
|
|
|
Some examples:
|
|
|
|
|
|
|
|
|
|
* single point of responsibility for certain cluster-wide consistent decisions, or
|
|
|
|
|
coordination of actions across the cluster system
|
|
|
|
|
* single entry point to an external system
|
|
|
|
|
* single master, many workers
|
|
|
|
|
* centralized naming service, or routing logic
|
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
Using a singleton should not be the first design choice. It has several drawbacks,
|
|
|
|
|
such as single-point of bottleneck. Single-point of failure is also a relevant concern,
|
|
|
|
|
but for some cases this feature takes care of that by making sure that another singleton
|
2013-01-14 14:09:53 +01:00
|
|
|
instance will eventually be started.
|
|
|
|
|
|
2013-03-14 20:32:43 +01:00
|
|
|
The cluster singleton pattern is implemented by ``akka.contrib.pattern.ClusterSingletonManager``.
|
2013-03-26 18:17:50 +01:00
|
|
|
It manages singleton actor instance among all cluster nodes or a group of nodes tagged with
|
2013-03-14 20:32:43 +01:00
|
|
|
a specific role. ``ClusterSingletonManager`` is an actor that is supposed to be started on
|
2013-03-26 18:17:50 +01:00
|
|
|
all nodes, or all nodes with specified role, in the cluster. The actual singleton actor is
|
2013-04-28 22:05:40 +02:00
|
|
|
started by the ``ClusterSingletonManager`` on the oldest node by creating a child actor from
|
2013-03-14 20:32:43 +01:00
|
|
|
supplied ``Props``. ``ClusterSingletonManager`` makes sure that at most one singleton instance
|
|
|
|
|
is running at any point in time.
|
2013-01-14 14:09:53 +01:00
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
The singleton actor is always running on the oldest member, which can be determined by
|
|
|
|
|
``Member#isOlderThan``. This can change when removing members. A graceful hand over can normally
|
|
|
|
|
be performed when current oldest node is leaving the cluster. Be aware that there is a short
|
|
|
|
|
time period when there is no active singleton during the hand-over process.
|
2013-01-14 14:09:53 +01:00
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
The cluster failure detector will notice when oldest node becomes unreachable due to
|
|
|
|
|
things like JVM crash, hard shut down, or network failure. Then a new oldest node will
|
2013-03-26 18:17:50 +01:00
|
|
|
take over and a new singleton actor is created. For these failure scenarios there will
|
|
|
|
|
not be a graceful hand-over, but more than one active singletons is prevented by all
|
2013-01-14 14:09:53 +01:00
|
|
|
reasonable means. Some corner cases are eventually resolved by configurable timeouts.
|
|
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
You access the singleton actor with ``actorSelection`` using the names you have
|
|
|
|
|
specified when creating the ClusterSingletonManager. You can subscribe to
|
|
|
|
|
``akka.cluster.ClusterEvent.MemberEvent`` and sort the members by age
|
|
|
|
|
(``Member#isOlderThan``) to keep track of oldest member.
|
2013-03-14 20:32:43 +01:00
|
|
|
Alternatively the singleton actor may broadcast its existence when it is started.
|
2013-01-14 14:09:53 +01:00
|
|
|
|
|
|
|
|
An Example
|
|
|
|
|
----------
|
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
Assume that we need one single entry point to an external system. An actor that
|
|
|
|
|
receives messages from a JMS queue with the strict requirement that only one
|
2013-01-14 14:09:53 +01:00
|
|
|
JMS consumer must exist to be make sure that the messages are processed in order.
|
|
|
|
|
That is perhaps not how one would like to design things, but a typical real-world
|
|
|
|
|
scenario when integrating with external systems.
|
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
On each node in the cluster you need to start the ``ClusterSingletonManager`` and
|
2013-01-14 14:09:53 +01:00
|
|
|
supply the ``Props`` of the singleton actor, in this case the JMS queue consumer.
|
|
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
In Scala:
|
|
|
|
|
|
2013-01-14 14:09:53 +01:00
|
|
|
.. includecode:: @contribSrc@/src/multi-jvm/scala/akka/contrib/pattern/ClusterSingletonManagerSpec.scala#create-singleton-manager
|
|
|
|
|
|
2013-03-14 20:32:43 +01:00
|
|
|
Here we limit the singleton to nodes tagged with the ``"worker"`` role, but all nodes, independent of
|
|
|
|
|
role, can be used by specifying ``None`` as ``role`` parameter.
|
|
|
|
|
|
2013-01-14 14:09:53 +01:00
|
|
|
The corresponding Java API for the ``singeltonProps`` function is ``akka.contrib.pattern.ClusterSingletonPropsFactory``.
|
2013-04-17 22:14:19 +02:00
|
|
|
The Java API takes a plain String for the role parameter and ``null`` means that all nodes, independent of
|
2013-03-14 20:32:43 +01:00
|
|
|
role, are used.
|
2013-01-14 14:09:53 +01:00
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
In Java:
|
|
|
|
|
|
|
|
|
|
.. includecode:: @contribSrc@/src/test/java/akka/contrib/pattern/ClusterSingletonManagerTest.java#create-singleton-manager
|
|
|
|
|
|
2013-05-24 14:43:01 +02:00
|
|
|
.. note::
|
|
|
|
|
|
|
|
|
|
The ``singletonProps``/``singletonPropsFactory`` is invoked when creating
|
|
|
|
|
the singleton actor and it must not use members that are not thread safe, e.g.
|
|
|
|
|
mutable state in enclosing actor.
|
|
|
|
|
|
2013-01-14 14:09:53 +01:00
|
|
|
Here we use an application specific ``terminationMessage`` to be able to close the
|
2013-03-26 18:17:50 +01:00
|
|
|
resources before actually stopping the singleton actor. Note that ``PoisonPill`` is a
|
2013-01-14 14:09:53 +01:00
|
|
|
perfectly fine ``terminationMessage`` if you only need to stop the actor.
|
|
|
|
|
|
|
|
|
|
Here is how the singleton actor handles the ``terminationMessage`` in this example.
|
|
|
|
|
|
|
|
|
|
.. includecode:: @contribSrc@/src/multi-jvm/scala/akka/contrib/pattern/ClusterSingletonManagerSpec.scala#consumer-end
|
|
|
|
|
|
|
|
|
|
Note that you can send back current state to the ``ClusterSingletonManager`` before terminating.
|
2013-04-28 22:05:40 +02:00
|
|
|
This message will be sent over to the ``ClusterSingletonManager`` at the new oldest node and it
|
2013-01-14 14:09:53 +01:00
|
|
|
will be passed to the ``singletonProps`` factory when creating the new singleton instance.
|
|
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
With the names given above the path of singleton actor can be constructed by subscribing to
|
2013-04-28 22:05:40 +02:00
|
|
|
``MemberEvent`` cluster event and sort the members by age to keep track of oldest member.
|
|
|
|
|
|
|
|
|
|
In Scala:
|
|
|
|
|
|
|
|
|
|
.. includecode:: @contribSrc@/src/multi-jvm/scala/akka/contrib/pattern/ClusterSingletonManagerSpec.scala#singleton-proxy
|
2013-03-14 20:32:43 +01:00
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
In Java:
|
2013-01-14 14:09:53 +01:00
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
.. includecode:: @contribSrc@/src/test/java/akka/contrib/pattern/ClusterSingletonManagerTest.java#singleton-proxy
|
|
|
|
|
|
|
|
|
|
The checks of ``role`` can be omitted if you don't limit the singleton to the group of members
|
|
|
|
|
tagged with a specific role.
|
2013-01-14 14:09:53 +01:00
|
|
|
|
2013-03-26 18:17:50 +01:00
|
|
|
Note that the hand-over might still be in progress and the singleton actor might not be started yet
|
2013-04-28 22:05:40 +02:00
|
|
|
when you receive the member event.
|
2013-01-28 08:47:52 +01:00
|
|
|
|
2013-04-06 16:22:30 +02:00
|
|
|
A nice alternative to the above proxy is to use :ref:`distributed-pub-sub`. Let the singleton
|
|
|
|
|
actor register itself to the mediator with ``DistributedPubSubMediator.Put`` message when it is
|
|
|
|
|
started. Send messages to the singleton actor via the mediator with ``DistributedPubSubMediator.SendToAll``.
|
|
|
|
|
|
2013-01-14 14:09:53 +01:00
|
|
|
.. note:: The singleton pattern will be simplified, perhaps provided out-of-the-box, when the cluster handles automatic actor partitioning.
|