various documentation improvements
- document DeathWatch - actorOf vs. actorFor
This commit is contained in:
parent
a917260488
commit
2fbef5b5ce
7 changed files with 228 additions and 22 deletions
|
|
@ -3,6 +3,10 @@
|
||||||
Actor References, Paths and Addresses
|
Actor References, Paths and Addresses
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
.. sidebar:: Contents
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
This chapter describes how actors are identified and located within a possibly
|
This chapter describes how actors are identified and located within a possibly
|
||||||
distributed actor system. It ties into the central idea that
|
distributed actor system. It ties into the central idea that
|
||||||
:ref:`actor-systems` form intrinsic supervision hierarchies as well as that
|
:ref:`actor-systems` form intrinsic supervision hierarchies as well as that
|
||||||
|
|
@ -225,6 +229,23 @@ extracting the sender references, and then watch all discovered concrete
|
||||||
actors. This scheme of resolving a selection may be improved upon in a future
|
actors. This scheme of resolving a selection may be improved upon in a future
|
||||||
release.
|
release.
|
||||||
|
|
||||||
|
.. _actorOf-vs-actorFor:
|
||||||
|
|
||||||
|
Summary: ``actorOf`` vs. ``actorFor``
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
What the above sections described in some detail can be summarized and
|
||||||
|
memorized easily as follows:
|
||||||
|
|
||||||
|
- ``actorOf`` only ever creates a new actor, and it creates it as a direct
|
||||||
|
child of the context on which this method is invoked (which may be any
|
||||||
|
actor or actor system).
|
||||||
|
|
||||||
|
- ``actorFor`` only ever looks up an existing actor, i.e. does not create
|
||||||
|
one.
|
||||||
|
|
||||||
The Interplay with Remote Deployment
|
The Interplay with Remote Deployment
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ import static akka.actor.Actors.*;
|
||||||
import akka.japi.Procedure;
|
import akka.japi.Procedure;
|
||||||
//#import-procedure
|
//#import-procedure
|
||||||
|
|
||||||
|
//#import-watch
|
||||||
|
import akka.actor.Terminated;
|
||||||
|
//#import-watch
|
||||||
|
|
||||||
import akka.actor.Props;
|
import akka.actor.Props;
|
||||||
import akka.actor.UntypedActor;
|
import akka.actor.UntypedActor;
|
||||||
import akka.actor.UntypedActorFactory;
|
import akka.actor.UntypedActorFactory;
|
||||||
|
|
@ -161,6 +165,15 @@ public class UntypedActorDocTestBase {
|
||||||
system.shutdown();
|
system.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void useWatch() {
|
||||||
|
ActorSystem system = ActorSystem.create("MySystem");
|
||||||
|
ActorRef myActor = system.actorOf(new Props(WatchActor.class));
|
||||||
|
Future<Object> future = myActor.ask("kill", 1000);
|
||||||
|
assert Await.result(future, Duration.parse("1 second")).equals("finished");
|
||||||
|
system.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
public static class MyActor extends UntypedActor {
|
public static class MyActor extends UntypedActor {
|
||||||
|
|
||||||
public MyActor(String s) {
|
public MyActor(String s) {
|
||||||
|
|
@ -251,4 +264,27 @@ public class UntypedActorDocTestBase {
|
||||||
}
|
}
|
||||||
//#hot-swap-actor
|
//#hot-swap-actor
|
||||||
|
|
||||||
|
//#watch
|
||||||
|
public static class WatchActor extends UntypedActor {
|
||||||
|
final ActorRef child = this.getContext().actorOf(Props.empty(), "child");
|
||||||
|
{
|
||||||
|
this.getContext().watch(child); // <-- this is the only call needed for registration
|
||||||
|
}
|
||||||
|
ActorRef lastSender = getContext().system().deadLetters();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Object message) {
|
||||||
|
if (message.equals("kill")) {
|
||||||
|
getContext().stop(child);
|
||||||
|
lastSender = getSender();
|
||||||
|
} else if (message instanceof Terminated) {
|
||||||
|
final Terminated t = (Terminated) message;
|
||||||
|
if (t.getActor() == child) {
|
||||||
|
lastSender.tell("finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#watch
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ its syntax from Erlang.
|
||||||
Creating Actors
|
Creating Actors
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
Since Akka enforces parental supervision every actor is supervised and
|
||||||
|
(potentially) the supervisor of its children; it is advisable that you
|
||||||
|
familiarize yourself with :ref:`actor-systems` and :ref:`supervision` and it
|
||||||
|
may also help to read :ref:`actorOf-vs-actorFor`.
|
||||||
|
|
||||||
Defining an Actor class
|
Defining an Actor class
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
@ -131,6 +135,7 @@ In addition, it offers:
|
||||||
* system that the actor belongs to
|
* system that the actor belongs to
|
||||||
* parent supervisor
|
* parent supervisor
|
||||||
* supervised children
|
* supervised children
|
||||||
|
* lifecycle monitoring
|
||||||
* hotswap behavior stack as described in :ref:`UntypedActor.HotSwap`
|
* hotswap behavior stack as described in :ref:`UntypedActor.HotSwap`
|
||||||
|
|
||||||
The remaining visible methods are user-overridable life-cycle hooks which are
|
The remaining visible methods are user-overridable life-cycle hooks which are
|
||||||
|
|
@ -141,6 +146,36 @@ described in the following:
|
||||||
The implementations shown above are the defaults provided by the :class:`UntypedActor`
|
The implementations shown above are the defaults provided by the :class:`UntypedActor`
|
||||||
class.
|
class.
|
||||||
|
|
||||||
|
.. _deathwatch-java:
|
||||||
|
|
||||||
|
Lifecycle Monitoring aka DeathWatch
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
In order to be notified when another actor terminates (i.e. stops permanently,
|
||||||
|
not temporary failure and restart), an actor may register itself for reception
|
||||||
|
of the :class:`Terminated` message dispatched by the other actor upon
|
||||||
|
termination (see `Stopping Actors`_). This service is provided by the
|
||||||
|
:class:`DeathWatch` component of the actor system.
|
||||||
|
|
||||||
|
Registering a monitor is easy (see fourth line, the rest is for demonstrating
|
||||||
|
the whole functionality):
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#watch
|
||||||
|
|
||||||
|
It should be noted that the :class:`Terminated` message is generated
|
||||||
|
independent of the order in which registration and termination occur.
|
||||||
|
Registering multiple times does not necessarily lead to multiple messages being
|
||||||
|
generated, but there is no guarantee that only exactly one such message is
|
||||||
|
received: if termination of the watched actor has generated and queued the
|
||||||
|
message, and another registration is done before this message has been
|
||||||
|
processed, then a second message will be queued, because registering for
|
||||||
|
monitoring of an already terminated actor leads to the immediate generation of
|
||||||
|
the :class:`Terminated` message.
|
||||||
|
|
||||||
|
It is also possible to deregister from watching another actor’s liveliness
|
||||||
|
using ``context.unwatch(target)``, but obviously this cannot guarantee
|
||||||
|
non-reception of the :class:`Terminated` message because that may already have
|
||||||
|
been queued.
|
||||||
|
|
||||||
Start Hook
|
Start Hook
|
||||||
----------
|
----------
|
||||||
|
|
@ -398,21 +433,44 @@ but additional messages in the mailbox will not be processed. By default these
|
||||||
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
|
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
|
||||||
depends on the mailbox implementation.
|
depends on the mailbox implementation.
|
||||||
|
|
||||||
When stop is called then a call to the ``def postStop`` callback method will
|
Termination of an actor proceeds in two steps: first the actor suspends its
|
||||||
take place. The ``Actor`` can use this callback to implement shutdown behavior.
|
mailbox processing and sends a stop command to all its children, then it keeps
|
||||||
|
processing the termination messages from its children until the last one is
|
||||||
|
gone, finally terminating itself (invoking :meth:`postStop`, dumping mailbox,
|
||||||
|
publishing :class:`Terminated` on the :ref:`DeathWatch <deathwatch-java>`, telling
|
||||||
|
its supervisor). This procedure ensures that actor system sub-trees terminate
|
||||||
|
in an orderly fashion, propagating the stop command to the leaves and
|
||||||
|
collecting their confirmation back to the stopped supervisor. If one of the
|
||||||
|
actors does not respond (i.e. processing a message for extended periods of time
|
||||||
|
and therefore not receiving the stop command), this whole process will be
|
||||||
|
stuck.
|
||||||
|
|
||||||
|
It is possible to disregard specific children with respect to shutdown
|
||||||
|
confirmation by stopping them explicitly before issuing the
|
||||||
|
``context.stop(self)``::
|
||||||
|
|
||||||
|
context.stop(someChild);
|
||||||
|
context.stop(self);
|
||||||
|
|
||||||
|
In this case ``someChild`` will be stopped asynchronously and re-parented to
|
||||||
|
the :class:`Locker`, where :class:`DavyJones` will keep tabs and dispose of it
|
||||||
|
eventually.
|
||||||
|
|
||||||
|
Upon :meth:`ActorSystem.shutdown()`, the system guardian actors will be
|
||||||
|
stopped, and the aforementioned process will ensure proper termination of the
|
||||||
|
whole system.
|
||||||
|
|
||||||
|
The :meth:`postStop()` hook is invoked after an actor is fully stopped. This
|
||||||
|
enables cleaning up of resources:
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
|
@Override
|
||||||
public void postStop() {
|
public void postStop() {
|
||||||
... // clean up resources
|
// close some file or database connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
All Actors are stopped when the ``ActorSystem`` is stopped.
|
|
||||||
Supervised actors are stopped when the supervisor is stopped, i.e. children are stopped
|
|
||||||
when parent is stopped.
|
|
||||||
|
|
||||||
|
|
||||||
PoisonPill
|
PoisonPill
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
@ -421,9 +479,6 @@ stop the actor when the message is processed. ``PoisonPill`` is enqueued as
|
||||||
ordinary messages and will be handled after messages that were already queued
|
ordinary messages and will be handled after messages that were already queued
|
||||||
in the mailbox.
|
in the mailbox.
|
||||||
|
|
||||||
If the ``PoisonPill`` was sent with ``ask``, the ``Future`` will be completed with an
|
|
||||||
``akka.actor.ActorKilledException("PoisonPill")``.
|
|
||||||
|
|
||||||
Use it like this:
|
Use it like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,18 @@ changes in client code. This API cleanup is planned to be the last one for a
|
||||||
significant amount of time.
|
significant amount of time.
|
||||||
|
|
||||||
Detailed migration guide will be written.
|
Detailed migration guide will be written.
|
||||||
|
|
||||||
|
Unordered Collection of Migration Items
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
``ActorRef.ask()``
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The mechanism for collecting an actor’s reply in a :class:`Future` has been
|
||||||
|
reworked for better location transparency: it uses an actor under the hood.
|
||||||
|
This actor needs to be disposable by the garbage collector in case no reply is
|
||||||
|
ever received, and the decision is based upon a timeout. This timeout
|
||||||
|
determines when the actor will stop itself and hence closes the window for a
|
||||||
|
reply to be received; it is independent of the timeout applied when awaiting
|
||||||
|
completion of the :class:`Future`, however, the actor will complete the
|
||||||
|
:class:`Future` with an :class:`AskTimeoutException` when it stops itself.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ its syntax from Erlang.
|
||||||
Creating Actors
|
Creating Actors
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
Since Akka enforces parental supervision every actor is supervised and
|
||||||
|
(potentially) the supervisor of its children; it is advisable that you
|
||||||
|
familiarize yourself with :ref:`actor-systems` and :ref:`supervision` and it
|
||||||
|
may also help to read :ref:`actorOf-vs-actorFor`.
|
||||||
|
|
||||||
Defining an Actor class
|
Defining an Actor class
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
@ -156,6 +160,7 @@ In addition, it offers:
|
||||||
* system that the actor belongs to
|
* system that the actor belongs to
|
||||||
* parent supervisor
|
* parent supervisor
|
||||||
* supervised children
|
* supervised children
|
||||||
|
* lifecycle monitoring
|
||||||
* hotswap behavior stack as described in :ref:`Actor.HotSwap`
|
* hotswap behavior stack as described in :ref:`Actor.HotSwap`
|
||||||
|
|
||||||
You can import the members in the :obj:`context` to avoid prefixing access with ``context.``
|
You can import the members in the :obj:`context` to avoid prefixing access with ``context.``
|
||||||
|
|
@ -176,6 +181,35 @@ described in the following::
|
||||||
The implementations shown above are the defaults provided by the :class:`Actor`
|
The implementations shown above are the defaults provided by the :class:`Actor`
|
||||||
trait.
|
trait.
|
||||||
|
|
||||||
|
.. _deathwatch-scala:
|
||||||
|
|
||||||
|
Lifecycle Monitoring aka DeathWatch
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
In order to be notified when another actor terminates (i.e. stops permanently,
|
||||||
|
not temporary failure and restart), an actor may register itself for reception
|
||||||
|
of the :class:`Terminated` message dispatched by the other actor upon
|
||||||
|
termination (see `Stopping Actors`_). This service is provided by the
|
||||||
|
:class:`DeathWatch` component of the actor system.
|
||||||
|
|
||||||
|
Registering a monitor is easy:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#watch
|
||||||
|
|
||||||
|
It should be noted that the :class:`Terminated` message is generated
|
||||||
|
independent of the order in which registration and termination occur.
|
||||||
|
Registering multiple times does not necessarily lead to multiple messages being
|
||||||
|
generated, but there is no guarantee that only exactly one such message is
|
||||||
|
received: if termination of the watched actor has generated and queued the
|
||||||
|
message, and another registration is done before this message has been
|
||||||
|
processed, then a second message will be queued, because registering for
|
||||||
|
monitoring of an already terminated actor leads to the immediate generation of
|
||||||
|
the :class:`Terminated` message.
|
||||||
|
|
||||||
|
It is also possible to deregister from watching another actor’s liveliness
|
||||||
|
using ``context.unwatch(target)``, but obviously this cannot guarantee
|
||||||
|
non-reception of the :class:`Terminated` message because that may already have
|
||||||
|
been queued.
|
||||||
|
|
||||||
Start Hook
|
Start Hook
|
||||||
----------
|
----------
|
||||||
|
|
@ -457,19 +491,42 @@ but additional messages in the mailbox will not be processed. By default these
|
||||||
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
|
messages are sent to the :obj:`deadLetters` of the :obj:`ActorSystem`, but that
|
||||||
depends on the mailbox implementation.
|
depends on the mailbox implementation.
|
||||||
|
|
||||||
When stop is called then a call to the ``def postStop`` callback method will
|
Termination of an actor proceeds in two steps: first the actor suspends its
|
||||||
take place. The ``Actor`` can use this callback to implement shutdown behavior.
|
mailbox processing and sends a stop command to all its children, then it keeps
|
||||||
|
processing the termination messages from its children until the last one is
|
||||||
|
gone, finally terminating itself (invoking :meth:`postStop`, dumping mailbox,
|
||||||
|
publishing :class:`Terminated` on the :ref:`DeathWatch <deathwatch-scala>`, telling
|
||||||
|
its supervisor). This procedure ensures that actor system sub-trees terminate
|
||||||
|
in an orderly fashion, propagating the stop command to the leaves and
|
||||||
|
collecting their confirmation back to the stopped supervisor. If one of the
|
||||||
|
actors does not respond (i.e. processing a message for extended periods of time
|
||||||
|
and therefore not receiving the stop command), this whole process will be
|
||||||
|
stuck.
|
||||||
|
|
||||||
|
It is possible to disregard specific children with respect to shutdown
|
||||||
|
confirmation by stopping them explicitly before issuing the
|
||||||
|
``context.stop(self)``::
|
||||||
|
|
||||||
|
context.stop(someChild)
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
In this case ``someChild`` will be stopped asynchronously and re-parented to
|
||||||
|
the :class:`Locker`, where :class:`DavyJones` will keep tabs and dispose of it
|
||||||
|
eventually.
|
||||||
|
|
||||||
|
Upon :meth:`ActorSystem.shutdown()`, the system guardian actors will be
|
||||||
|
stopped, and the aforementioned process will ensure proper termination of the
|
||||||
|
whole system.
|
||||||
|
|
||||||
|
The :meth:`postStop()` hook is invoked after an actor is fully stopped. This
|
||||||
|
enables cleaning up of resources:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. code-block:: scala
|
||||||
|
|
||||||
override def postStop() = {
|
override def postStop() = {
|
||||||
... // clean up resources
|
// close some file or database connection
|
||||||
}
|
}
|
||||||
|
|
||||||
All Actors are stopped when the ``ActorSystem`` is stopped.
|
|
||||||
Supervised actors are stopped when the supervisor is stopped, i.e. children are stopped
|
|
||||||
when parent is stopped.
|
|
||||||
|
|
||||||
|
|
||||||
PoisonPill
|
PoisonPill
|
||||||
----------
|
----------
|
||||||
|
|
@ -479,10 +536,6 @@ stop the actor when the message is processed. ``PoisonPill`` is enqueued as
|
||||||
ordinary messages and will be handled after messages that were already queued
|
ordinary messages and will be handled after messages that were already queued
|
||||||
in the mailbox.
|
in the mailbox.
|
||||||
|
|
||||||
If the ``PoisonPill`` was sent with ``?``, the ``Future`` will be completed with an
|
|
||||||
``akka.actor.ActorKilledException("PoisonPill")``.
|
|
||||||
|
|
||||||
|
|
||||||
.. _Actor.HotSwap:
|
.. _Actor.HotSwap:
|
||||||
|
|
||||||
Become/Unbecome
|
Become/Unbecome
|
||||||
|
|
|
||||||
|
|
@ -296,4 +296,25 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
|
|
||||||
val actor = system.actorOf(Props(new HotSwapActor), name = "hot")
|
val actor = system.actorOf(Props(new HotSwapActor), name = "hot")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"using watch" in {
|
||||||
|
//#watch
|
||||||
|
import akka.actor.{ Actor, Props, Terminated }
|
||||||
|
|
||||||
|
class WatchActor extends Actor {
|
||||||
|
val child = context.actorOf(Props.empty, "child")
|
||||||
|
context.watch(child) // <-- this is the only call needed for registration
|
||||||
|
var lastSender = system.deadLetters
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
case "kill" ⇒ context.stop(child); lastSender = sender
|
||||||
|
case Terminated(`child`) ⇒ lastSender ! "finished"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#watch
|
||||||
|
val a = system.actorOf(Props(new WatchActor))
|
||||||
|
implicit val sender = testActor
|
||||||
|
a ! "kill"
|
||||||
|
expectMsg("finished")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,11 @@ This enables the remoting by installing the :class:`RemoteActorRefProvider` and
|
||||||
chooses the default remote transport. All other options will be set
|
chooses the default remote transport. All other options will be set
|
||||||
specifically for each show case.
|
specifically for each show case.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Be sure to replace the default IP 127.0.0.1 with the real address the system
|
||||||
|
is reachable by if you deploy onto multiple machines!
|
||||||
|
|
||||||
.. _remote-lookup-sample:
|
.. _remote-lookup-sample:
|
||||||
|
|
||||||
Remote Lookup
|
Remote Lookup
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue