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
|
||||
=====================================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
This chapter describes how actors are identified and located within a possibly
|
||||
distributed actor system. It ties into the central idea 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
|
||||
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
|
||||
------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ import static akka.actor.Actors.*;
|
|||
import akka.japi.Procedure;
|
||||
//#import-procedure
|
||||
|
||||
//#import-watch
|
||||
import akka.actor.Terminated;
|
||||
//#import-watch
|
||||
|
||||
import akka.actor.Props;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.UntypedActorFactory;
|
||||
|
|
@ -161,6 +165,15 @@ public class UntypedActorDocTestBase {
|
|||
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 MyActor(String s) {
|
||||
|
|
@ -251,4 +264,27 @@ public class UntypedActorDocTestBase {
|
|||
}
|
||||
//#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
|
||||
===============
|
||||
|
||||
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
|
||||
-----------------------
|
||||
|
|
@ -131,6 +135,7 @@ In addition, it offers:
|
|||
* system that the actor belongs to
|
||||
* parent supervisor
|
||||
* supervised children
|
||||
* lifecycle monitoring
|
||||
* hotswap behavior stack as described in :ref:`UntypedActor.HotSwap`
|
||||
|
||||
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`
|
||||
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
|
||||
----------
|
||||
|
|
@ -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
|
||||
depends on the mailbox implementation.
|
||||
|
||||
When stop is called then a call to the ``def postStop`` callback method will
|
||||
take place. The ``Actor`` can use this callback to implement shutdown behavior.
|
||||
Termination of an actor proceeds in two steps: first the actor suspends its
|
||||
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
|
||||
|
||||
@Override
|
||||
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
|
||||
----------
|
||||
|
||||
|
|
@ -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
|
||||
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:
|
||||
|
||||
.. 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.
|
||||
|
||||
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
|
||||
===============
|
||||
|
||||
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
|
||||
-----------------------
|
||||
|
|
@ -156,6 +160,7 @@ In addition, it offers:
|
|||
* system that the actor belongs to
|
||||
* parent supervisor
|
||||
* supervised children
|
||||
* lifecycle monitoring
|
||||
* hotswap behavior stack as described in :ref:`Actor.HotSwap`
|
||||
|
||||
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`
|
||||
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
|
||||
----------
|
||||
|
|
@ -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
|
||||
depends on the mailbox implementation.
|
||||
|
||||
When stop is called then a call to the ``def postStop`` callback method will
|
||||
take place. The ``Actor`` can use this callback to implement shutdown behavior.
|
||||
Termination of an actor proceeds in two steps: first the actor suspends its
|
||||
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
|
||||
|
||||
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
|
||||
----------
|
||||
|
|
@ -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
|
||||
in the mailbox.
|
||||
|
||||
If the ``PoisonPill`` was sent with ``?``, the ``Future`` will be completed with an
|
||||
``akka.actor.ActorKilledException("PoisonPill")``.
|
||||
|
||||
|
||||
.. _Actor.HotSwap:
|
||||
|
||||
Become/Unbecome
|
||||
|
|
|
|||
|
|
@ -296,4 +296,25 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
|||
|
||||
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
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue