2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-05-10 09:53:58 +02:00
|
|
|
|
.. _actors-scala:
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
################
|
|
|
|
|
|
Actors (Scala)
|
|
|
|
|
|
################
|
|
|
|
|
|
|
2011-04-26 21:11:58 +02:00
|
|
|
|
|
|
|
|
|
|
.. sidebar:: Contents
|
|
|
|
|
|
|
|
|
|
|
|
.. contents:: :local:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
The `Actor Model`_ provides a higher level of abstraction for writing concurrent
|
|
|
|
|
|
and distributed systems. It alleviates the developer from having to deal with
|
|
|
|
|
|
explicit locking and thread management, making it easier to write correct
|
|
|
|
|
|
concurrent and parallel systems. Actors were defined in the 1973 paper by Carl
|
|
|
|
|
|
Hewitt but have been popularized by the Erlang language, and used for example at
|
|
|
|
|
|
Ericsson with great success to build highly concurrent and reliable telecom
|
|
|
|
|
|
systems.
|
|
|
|
|
|
|
|
|
|
|
|
The API of Akka’s Actors is similar to Scala Actors which has borrowed some of
|
|
|
|
|
|
its syntax from Erlang.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
.. _Actor Model: http://en.wikipedia.org/wiki/Actor_model
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating Actors
|
2011-10-05 17:41:00 +02:00
|
|
|
|
===============
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Actors can be created either by:
|
2011-04-11 21:12:44 -06:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
* Extending the Actor class and implementing the receive method.
|
|
|
|
|
|
* Create an anonymous actor using one of the actor methods.
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Defining an Actor class
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-----------------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Actor classes are implemented by extending the Actor class and implementing the
|
|
|
|
|
|
``receive`` method. The ``receive`` method should define a series of case
|
|
|
|
|
|
statements (which has the type ``PartialFunction[Any, Unit]``) that defines
|
|
|
|
|
|
which messages your Actor can handle, using standard Scala pattern matching,
|
|
|
|
|
|
along with the implementation of how the messages should be processed.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Here is an example:
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
.. includecode:: code/ActorDocSpec.scala
|
|
|
|
|
|
:include: imports,my-actor
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Please note that the Akka Actor ``receive`` message loop is exhaustive, which is
|
|
|
|
|
|
different compared to Erlang and Scala Actors. This means that you need to
|
|
|
|
|
|
provide a pattern match for all messages that it can accept and if you want to
|
|
|
|
|
|
be able to handle unknown messages then you need to have a default case as in
|
|
|
|
|
|
the example above.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating Actors
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
.. includecode:: code/ActorDocSpec.scala#creating-actorOf
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
Normally you would want to import the ``actorOf`` method like this:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
.. includecode:: code/ActorDocSpec.scala#creating-imported
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
to avoid prefixing it with ``Actor`` every time you use it.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
The call to ``actorOf`` returns an instance of ``ActorRef``. This is a handle to
|
|
|
|
|
|
the ``Actor`` instance which you can use to interact with the ``Actor``. The
|
|
|
|
|
|
``ActorRef`` is immutable and has a one to one relationship with the Actor it
|
|
|
|
|
|
represents. The ``ActorRef`` is also serializable and network-aware. This means
|
|
|
|
|
|
that you can serialize it, send it over the wire and use it on a remote host and
|
|
|
|
|
|
it will still be representing the same Actor on the original node, across the
|
|
|
|
|
|
network.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating Actors with non-default constructor
|
2011-10-05 17:41:00 +02:00
|
|
|
|
--------------------------------------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
If your Actor has a constructor that takes parameters then you can't create it
|
|
|
|
|
|
using ``actorOf[TYPE]``. Instead you can use a variant of ``actorOf`` that takes
|
|
|
|
|
|
a call-by-name block in which you can create the Actor in any way you like.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Here is an example:
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
.. includecode:: code/ActorDocSpec.scala#creating-constructor
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Running a block of code asynchronously
|
2011-10-05 17:41:00 +02:00
|
|
|
|
--------------------------------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Here we create a light-weight actor-based thread, that can be used to spawn off
|
|
|
|
|
|
a task. Code blocks spawned up like this are always implicitly started, shut
|
|
|
|
|
|
down and made eligible for garbage collection. The actor that is created "under
|
|
|
|
|
|
the hood" is not reachable from the outside and there is no way of sending
|
|
|
|
|
|
messages to it. It being an actor is only an implementation detail. It will only
|
|
|
|
|
|
run the block in an event-based thread and exit once the block has run to
|
|
|
|
|
|
completion.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
spawn {
|
|
|
|
|
|
... // do stuff
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-06-27 19:09:09 +02:00
|
|
|
|
Actor Internal API
|
2011-10-05 17:41:00 +02:00
|
|
|
|
==================
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
The :class:`Actor` trait defines only one abstract method, the above mentioned
|
2011-06-27 19:09:09 +02:00
|
|
|
|
:meth:`receive`. In addition, it offers two convenience methods
|
|
|
|
|
|
:meth:`become`/:meth:`unbecome` for modifying the hotswap behavior stack as
|
|
|
|
|
|
described in :ref:`Actor.HotSwap` and the :obj:`self` reference to this actor’s
|
2011-06-27 22:20:09 +02:00
|
|
|
|
:class:`ActorRef` object. If the current actor behavior does not match a
|
|
|
|
|
|
received message, :meth:`unhandled` is called, which by default throws an
|
|
|
|
|
|
:class:`UnhandledMessageException`.
|
|
|
|
|
|
|
|
|
|
|
|
The remaining visible methods are user-overridable life-cycle hooks which are
|
|
|
|
|
|
described in the following::
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
|
|
|
|
|
def preStart() {}
|
2011-06-27 22:20:09 +02:00
|
|
|
|
def preRestart(cause: Throwable, message: Option[Any]) {}
|
2011-06-27 19:09:09 +02:00
|
|
|
|
def postRestart(cause: Throwable) {}
|
|
|
|
|
|
def postStop() {}
|
|
|
|
|
|
|
|
|
|
|
|
The implementations shown above are the defaults provided by the :class:`Actor`
|
|
|
|
|
|
trait.
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-06-27 19:09:09 +02:00
|
|
|
|
Start Hook
|
2011-10-05 17:41:00 +02:00
|
|
|
|
----------
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Right after starting the actor, its :meth:`preStart` method is invoked.
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
|
|
override def preStart {
|
2011-10-05 17:41:00 +02:00
|
|
|
|
// registering with other actors
|
2011-06-27 19:09:09 +02:00
|
|
|
|
someService ! Register(self)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-06-27 19:09:09 +02:00
|
|
|
|
Restart Hooks
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-------------
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
|
|
|
|
|
A supervised actor, i.e. one which is linked to another actor with a fault
|
|
|
|
|
|
handling strategy, will be restarted in case an exception is thrown while
|
|
|
|
|
|
processing a message. This restart involves four of the hooks mentioned above:
|
|
|
|
|
|
|
|
|
|
|
|
1. The old actor is informed by calling :meth:`preRestart` with the exception
|
2011-06-27 22:20:09 +02:00
|
|
|
|
which caused the restart and the message which triggered that exception; the
|
|
|
|
|
|
latter may be ``None`` if the restart was not caused by processing a
|
|
|
|
|
|
message, e.g. when a supervisor does not trap the exception and is restarted
|
|
|
|
|
|
in turn by its supervisor. This method is the best place for cleaning up,
|
2011-06-27 19:09:09 +02:00
|
|
|
|
preparing hand-over to the fresh actor instance, etc.
|
2011-09-26 11:08:45 +02:00
|
|
|
|
2. The initial factory from the ``Actor.actorOf`` call is used
|
|
|
|
|
|
to produce the fresh instance.
|
2011-06-27 19:09:09 +02:00
|
|
|
|
3. The new actor’s :meth:`preStart` method is invoked, just as in the normal
|
|
|
|
|
|
start-up case.
|
|
|
|
|
|
4. The new actor’s :meth:`postRestart` method is called with the exception
|
|
|
|
|
|
which caused the restart.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
An actor restart replaces only the actual actor object; the contents of the
|
|
|
|
|
|
mailbox and the hotswap stack are unaffected by the restart, so processing of
|
|
|
|
|
|
messages will resume after the :meth:`postRestart` hook returns. Any message
|
2011-06-27 22:20:09 +02:00
|
|
|
|
sent to an actor while it is being restarted will be queued to its mailbox as
|
2011-06-27 19:09:09 +02:00
|
|
|
|
usual.
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-06-27 19:09:09 +02:00
|
|
|
|
Stop Hook
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------
|
2011-06-27 19:09:09 +02:00
|
|
|
|
|
|
|
|
|
|
After stopping an actor, its :meth:`postStop` hook is called, which may be used
|
|
|
|
|
|
e.g. for deregistering this actor from other services. This hook is guaranteed
|
|
|
|
|
|
to run after message queuing has been disabled for this actor, i.e. sending
|
|
|
|
|
|
messages would fail with an :class:`IllegalActorStateException`.
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Identifying Actors
|
|
|
|
|
|
==================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
An actor is identified by its address. If no address is associated with an actor
|
|
|
|
|
|
then a unique identifier is used instead. The address of an actor can be
|
|
|
|
|
|
accessed using ``self.address``.
|
2011-04-11 21:12:44 -06:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Messages and immutability
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=========================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
**IMPORTANT**: Messages can be any kind of object but have to be
|
|
|
|
|
|
immutable. Scala can’t enforce immutability (yet) so this has to be by
|
|
|
|
|
|
convention. Primitives like String, Int, Boolean are always immutable. Apart
|
|
|
|
|
|
from these the recommended approach is to use Scala case classes which are
|
|
|
|
|
|
immutable (if you don’t explicitly expose the state) and works great with
|
|
|
|
|
|
pattern matching at the receiver side.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Here is an example:
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
// define the case class
|
|
|
|
|
|
case class Register(user: User)
|
|
|
|
|
|
|
|
|
|
|
|
// create a new case class message
|
|
|
|
|
|
val message = Register(user)
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Other good messages types are ``scala.Tuple2``, ``scala.List``, ``scala.Map``
|
|
|
|
|
|
which are all immutable and great for pattern matching.
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Send messages
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=============
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
Messages are sent to an Actor through one of the following methods.
|
2011-04-11 21:12:44 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
* ``!`` means “fire-and-forget”, e.g. send a message asynchronously and return
|
|
|
|
|
|
immediately.
|
|
|
|
|
|
* ``?`` sends a message asynchronously and returns a :class:`Future`
|
|
|
|
|
|
representing a possible reply.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
.. note::
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
There used to be two more “bang” methods, which are now removed in Akka 2.0:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
* ``!!`` was similar to the current ``(actor ? msg).as[T]``; deprecation
|
|
|
|
|
|
followed from the change of timeout handling described below.
|
|
|
|
|
|
* ``!!![T]`` was similar to the current ``(actor ? msg).mapTo[T]``, with the
|
|
|
|
|
|
same change in the handling of :class:`Future`’s timeout as for ``!!``, but
|
|
|
|
|
|
additionally the old method could defer possible type cast problems into
|
|
|
|
|
|
seemingly unrelated parts of the code base.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-09-21 23:41:52 +02:00
|
|
|
|
Message ordering is guaranteed on a per-sender basis.
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Fire-forget
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-----------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
This is the preferred way of sending messages. No blocking waiting for a
|
|
|
|
|
|
message. This gives the best concurrency and scalability characteristics.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
actor ! "hello"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
If invoked from within an Actor, then the sending actor reference will be
|
|
|
|
|
|
implicitly passed along with the message and available to the receiving Actor
|
|
|
|
|
|
in its ``channel: UntypedChannel`` member field. The target actor can use this
|
|
|
|
|
|
to reply to the original sender, e.g. by using the ``self.reply(message: Any)``
|
|
|
|
|
|
method.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
If invoked from an instance that is **not** an Actor there will be no implicit
|
|
|
|
|
|
sender passed along with the message and you will get an
|
|
|
|
|
|
IllegalActorStateException when calling ``self.reply(...)``.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
Send-And-Receive-Future
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-----------------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
Using ``?`` will send a message to the receiving Actor asynchronously and
|
|
|
|
|
|
will return a :class:`Future`:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
.. code-block:: scala
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
val future = actor ? "hello"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
The receiving actor should reply to this message, which will complete the
|
|
|
|
|
|
future with the reply message as value; if the actor throws an exception while
|
|
|
|
|
|
processing the invocation, this exception will also complete the future. If the
|
|
|
|
|
|
actor does not complete the future, it will expire after the timeout period,
|
|
|
|
|
|
which is taken from one of the following three locations in order of
|
|
|
|
|
|
precedence:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
#. explicitly given timeout as in ``actor.?("hello")(timeout = 12 millis)``
|
|
|
|
|
|
#. implicit argument of type :class:`Actor.Timeout`, e.g.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
::
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
implicit val timeout = Actor.Timeout(12 millis)
|
|
|
|
|
|
val future = actor ? "hello"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
#. default timeout from ``akka.conf``
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
See :ref:`futures-scala` for more information on how to await or query a
|
|
|
|
|
|
future.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
Send-And-Receive-Eventually
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------------------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
The future returned from the ``?`` method can conveniently be passed around or
|
|
|
|
|
|
chained with further processing steps, but sometimes you just need the value,
|
|
|
|
|
|
even if that entails waiting for it (but keep in mind that waiting inside an
|
|
|
|
|
|
actor is prone to dead-locks, e.g. if obtaining the result depends on
|
|
|
|
|
|
processing another message on this actor).
|
|
|
|
|
|
|
|
|
|
|
|
For this purpose, there is the method :meth:`Future.as[T]` which waits until
|
|
|
|
|
|
either the future is completed or its timeout expires, whichever comes first.
|
|
|
|
|
|
The result is then inspected and returned as :class:`Some[T]` if it was
|
2011-08-09 21:37:39 +02:00
|
|
|
|
normally completed and the answer’s runtime type matches the desired type; if
|
|
|
|
|
|
the future contains an exception or the value cannot be cast to the desired
|
|
|
|
|
|
type, it will throw the exception or a :class:`ClassCastException` (if you want
|
|
|
|
|
|
to get :obj:`None` in the latter case, use :meth:`Future.asSilently[T]`). In
|
|
|
|
|
|
case of a timeout, :obj:`None` is returned.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
(actor ? msg).as[String] match {
|
|
|
|
|
|
case Some(answer) => ...
|
|
|
|
|
|
case None => ...
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val resultOption = (actor ? msg).as[String]
|
|
|
|
|
|
if (resultOption.isDefined) ... else ...
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-05-23 20:12:05 +02:00
|
|
|
|
for (x <- (actor ? msg).as[Int]) yield { 2 * x }
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Forward message
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
You can forward a message from one actor to another. This means that the
|
|
|
|
|
|
original sender address/reference is maintained even though the message is going
|
|
|
|
|
|
through a 'mediator'. This can be useful when writing actors that work as
|
|
|
|
|
|
routers, load-balancers, replicators etc.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
actor.forward(message)
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Receive messages
|
2011-10-05 17:41:00 +02:00
|
|
|
|
================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
An Actor has to implement the ``receive`` method to receive messages:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
protected def receive: PartialFunction[Any, Unit]
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Note: Akka has an alias to the ``PartialFunction[Any, Unit]`` type called
|
|
|
|
|
|
``Receive`` (``akka.actor.Actor.Receive``), so you can use this type instead for
|
|
|
|
|
|
clarity. But most often you don't need to spell it out.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
This method should return a ``PartialFunction``, e.g. a ‘match/case’ clause in
|
|
|
|
|
|
which the message can be matched against the different case clauses using Scala
|
|
|
|
|
|
pattern matching. Here is an example:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
class MyActor extends Actor {
|
|
|
|
|
|
def receive = {
|
|
|
|
|
|
case "Hello" =>
|
|
|
|
|
|
log.info("Received 'Hello'")
|
|
|
|
|
|
|
|
|
|
|
|
case _ =>
|
|
|
|
|
|
throw new RuntimeException("unknown message")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Reply to messages
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Reply using the sender
|
|
|
|
|
|
----------------------
|
2011-05-08 16:59:17 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
If you want to have a handle for replying to a message, you can use
|
|
|
|
|
|
``context.sender``, which gives you an ActorRef. You can reply by sending to
|
|
|
|
|
|
that ActorRef with ``context.sender ! Message``. You can also store the ActorRef
|
|
|
|
|
|
for replying later, or passing on to other actors. If there is no sender (a
|
|
|
|
|
|
message was sent without an actor or future context) then the context.sender
|
|
|
|
|
|
defaults to a 'dead-letter' actor ref.
|
2011-05-08 16:59:17 +02:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
case request =>
|
|
|
|
|
|
val result = process(request)
|
2011-10-05 17:41:00 +02:00
|
|
|
|
context.sender ! result // will have dead-letter actor as default
|
|
|
|
|
|
context.sender tryTell result // will return Boolean whether reply succeeded
|
2011-05-08 16:59:17 +02:00
|
|
|
|
|
2011-06-15 21:04:33 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Reply using the reply method
|
|
|
|
|
|
----------------------------
|
2011-05-08 16:59:17 +02:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
If you want to send a message back to the original sender of the message you
|
|
|
|
|
|
just received then you can use the ``context.reply(..)`` method.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
case request =>
|
|
|
|
|
|
val result = process(request)
|
2011-10-05 17:41:00 +02:00
|
|
|
|
context.reply(result)
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
In this case the ``result`` will be sent back to the Actor that sent the
|
|
|
|
|
|
``request``. This is equivalent to using ``context.sender ! result``.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Initial receive timeout
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=======================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
A timeout mechanism can be used to receive a message when no initial message is
|
|
|
|
|
|
received within a certain time. To receive this timeout you have to set the
|
|
|
|
|
|
``receiveTimeout`` property and declare a case handing the ReceiveTimeout
|
|
|
|
|
|
object.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
context.receiveTimeout = Some(30000L) // 30 seconds
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
def receive = {
|
|
|
|
|
|
case "Hello" =>
|
|
|
|
|
|
log.info("Received 'Hello'")
|
|
|
|
|
|
case ReceiveTimeout =>
|
|
|
|
|
|
throw new RuntimeException("received timeout")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
This mechanism also work for hotswapped receive functions. Every time a
|
|
|
|
|
|
``HotSwap`` is sent, the receive timeout is reset and rescheduled.
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Starting actors
|
2011-10-05 17:41:00 +02:00
|
|
|
|
===============
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-09-08 11:02:17 +02:00
|
|
|
|
Actors are created & started by invoking the ``actorOf`` method.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
val actor = actorOf[MyActor]
|
2011-09-08 11:02:17 +02:00
|
|
|
|
actor
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
When you create the ``Actor`` then it will automatically call the ``def
|
|
|
|
|
|
preStart`` callback method on the ``Actor`` trait. This is an excellent place to
|
|
|
|
|
|
add initialization code for the actor.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-04-26 20:31:08 +02:00
|
|
|
|
override def preStart() = {
|
2011-04-11 21:12:44 -06:00
|
|
|
|
... // initialization code
|
2011-04-09 19:55:46 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Stopping actors
|
2011-10-05 17:41:00 +02:00
|
|
|
|
===============
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
Actors are stopped by invoking the ``stop`` method.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-04-12 10:53:56 +02:00
|
|
|
|
actor.stop()
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
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.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-04-26 20:31:08 +02:00
|
|
|
|
override def postStop() = {
|
2011-04-09 19:55:46 -06:00
|
|
|
|
... // clean up resources
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PoisonPill
|
2011-10-05 17:41:00 +02:00
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
|
|
You can also send an actor the ``akka.actor.PoisonPill`` message, which will
|
|
|
|
|
|
stop the actor when the message is processed.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
If the sender is a ``Future`` (e.g. the message is sent with ``?``), the
|
|
|
|
|
|
``Future`` will be completed with an
|
|
|
|
|
|
``akka.actor.ActorKilledException("PoisonPill")``.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
|
2011-06-27 19:09:09 +02:00
|
|
|
|
.. _Actor.HotSwap:
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
HotSwap
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=======
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Upgrade
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Akka supports hotswapping the Actor’s message loop (e.g. its implementation) at
|
|
|
|
|
|
runtime. There are two ways you can do that:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
* Send a ``HotSwap`` message to the Actor.
|
|
|
|
|
|
* Invoke the ``become`` method from within the Actor.
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Both of these takes a ``ActorRef => PartialFunction[Any, Unit]`` that implements
|
|
|
|
|
|
the new message handler. The hotswapped code is kept in a Stack which can be
|
|
|
|
|
|
pushed and popped.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
To hotswap the Actor body using the ``HotSwap`` message:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
actor ! HotSwap( context => {
|
|
|
|
|
|
case message => context reply "hotswapped body"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
})
|
|
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
To hotswap the Actor using ``become``:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
def angry: Receive = {
|
2011-10-05 17:41:00 +02:00
|
|
|
|
case "foo" => context reply "I am already angry?"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
case "bar" => become(happy)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def happy: Receive = {
|
2011-10-05 17:41:00 +02:00
|
|
|
|
case "bar" => context reply "I am already happy :-)"
|
2011-04-09 19:55:46 -06:00
|
|
|
|
case "foo" => become(angry)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def receive = {
|
|
|
|
|
|
case "foo" => become(angry)
|
|
|
|
|
|
case "bar" => become(happy)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
The ``become`` method is useful for many different things, but a particular nice
|
|
|
|
|
|
example of it is in example where it is used to implement a Finite State Machine
|
|
|
|
|
|
(FSM): `Dining Hakkers`_.
|
|
|
|
|
|
|
|
|
|
|
|
.. _Dining Hakkers: http://github.com/jboner/akka/blob/master/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
Here is another little cute example of ``become`` and ``unbecome`` in action:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
case object Swap
|
|
|
|
|
|
class Swapper extends Actor {
|
|
|
|
|
|
def receive = {
|
|
|
|
|
|
case Swap =>
|
|
|
|
|
|
println("Hi")
|
|
|
|
|
|
become {
|
|
|
|
|
|
case Swap =>
|
|
|
|
|
|
println("Ho")
|
2011-04-12 11:03:36 +02:00
|
|
|
|
unbecome() // resets the latest 'become' (just for fun)
|
2011-04-09 19:55:46 -06:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-09-08 11:02:17 +02:00
|
|
|
|
val swap = actorOf[Swapper]
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
swap ! Swap // prints Hi
|
|
|
|
|
|
swap ! Swap // prints Ho
|
|
|
|
|
|
swap ! Swap // prints Hi
|
|
|
|
|
|
swap ! Swap // prints Ho
|
|
|
|
|
|
swap ! Swap // prints Hi
|
|
|
|
|
|
swap ! Swap // prints Ho
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
|
|
|
|
|
Encoding Scala Actors nested receives without accidentally leaking memory
|
|
|
|
|
|
-------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
See this `Unnested receive example <https://gist.github.com/797035>`_.
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Downgrade
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Since the hotswapped code is pushed to a Stack you can downgrade the code as
|
|
|
|
|
|
well. There are two ways you can do that:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
* Send the Actor a ``RevertHotswap`` message
|
|
|
|
|
|
* Invoke the ``unbecome`` method from within the Actor.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
Both of these will pop the Stack and replace the Actor's implementation with the
|
|
|
|
|
|
``PartialFunction[Any, Unit]`` that is at the top of the Stack.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
Revert the Actor body using the ``RevertHotSwap`` message:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
actor ! RevertHotSwap
|
|
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
Revert the Actor body using the ``unbecome`` method:
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
def receive: Receive = {
|
2011-04-12 11:03:36 +02:00
|
|
|
|
case "revert" => unbecome()
|
2011-04-09 19:55:46 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Killing an Actor
|
2011-10-05 17:41:00 +02:00
|
|
|
|
================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
You can kill an actor by sending a ``Kill`` message. This will restart the actor
|
|
|
|
|
|
through regular supervisor semantics.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
Use it like this:
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
// kill the actor called 'victim'
|
|
|
|
|
|
victim ! Kill
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-07-15 08:54:44 +03:00
|
|
|
|
Actors and exceptions
|
2011-10-05 17:41:00 +02:00
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
|
|
It can happen that while a message is being processed by an actor, that some
|
|
|
|
|
|
kind of exception is thrown, e.g. a database exception.
|
2011-07-15 08:54:44 +03:00
|
|
|
|
|
|
|
|
|
|
What happens to the Message
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------------------------
|
2011-07-15 08:41:42 +03:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
If an exception is thrown while a message is being processed (so taken of his
|
|
|
|
|
|
mailbox and handed over the the receive), then this message will be lost. It is
|
|
|
|
|
|
important to understand that it is not put back on the mailbox. So if you want
|
|
|
|
|
|
to retry processing of a message, you need to deal with it yourself by catching
|
|
|
|
|
|
the exception and retry your flow. Make sure that you put a bound on the number
|
|
|
|
|
|
of retries since you don't want a system to livelock (so consuming a lot of cpu
|
|
|
|
|
|
cycles without making progress).
|
2011-07-15 08:41:42 +03:00
|
|
|
|
|
2011-07-15 08:54:44 +03:00
|
|
|
|
What happens to the mailbox
|
2011-10-05 17:41:00 +02:00
|
|
|
|
---------------------------
|
|
|
|
|
|
|
|
|
|
|
|
If an exception is thrown while a message is being processed, nothing happens to
|
|
|
|
|
|
the mailbox. If the actor is restarted, the same mailbox will be there. So all
|
|
|
|
|
|
messages on that mailbox, will be there as well.
|
2011-07-15 08:54:44 +03:00
|
|
|
|
|
|
|
|
|
|
What happens to the actor
|
2011-10-05 17:41:00 +02:00
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
|
|
|
|
If an exception is thrown and the actor is supervised, the actor object itself
|
|
|
|
|
|
is discarded and a new instance is created. This new instance will now be used
|
|
|
|
|
|
in the actor references to this actor (so this is done invisible to the
|
|
|
|
|
|
developer).
|
|
|
|
|
|
|
|
|
|
|
|
If the actor is _not_ supervised, but its lifeCycle is set to Permanent
|
|
|
|
|
|
(default), it will just keep on processing messages as if nothing had happened.
|
|
|
|
|
|
|
|
|
|
|
|
If the actor is _not_ supervised, but its lifeCycle is set to Temporary, it will
|
|
|
|
|
|
be stopped immediately.
|
2011-07-15 08:54:44 +03:00
|
|
|
|
|
|
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
Extending Actors using PartialFunction chaining
|
2011-10-05 17:41:00 +02:00
|
|
|
|
===============================================
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
2011-10-05 17:41:00 +02:00
|
|
|
|
A bit advanced but very useful way of defining a base message handler and then
|
|
|
|
|
|
extend that, either through inheritance or delegation, is to use
|
|
|
|
|
|
``PartialFunction.orElse`` chaining.
|
2011-04-09 19:55:46 -06:00
|
|
|
|
|
|
|
|
|
|
In generic base Actor:
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
2011-04-11 14:41:17 +02:00
|
|
|
|
import akka.actor.Actor.Receive
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
abstract class GenericActor extends Actor {
|
|
|
|
|
|
// to be defined in subclassing actor
|
2011-04-11 14:41:17 +02:00
|
|
|
|
def specificMessageHandler: Receive
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
// generic message handler
|
2011-04-11 14:41:17 +02:00
|
|
|
|
def genericMessageHandler: Receive = {
|
|
|
|
|
|
case event => printf("generic: %s\n", event)
|
2011-04-09 19:55:46 -06:00
|
|
|
|
}
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-04-09 19:55:46 -06:00
|
|
|
|
def receive = specificMessageHandler orElse genericMessageHandler
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
In subclassing Actor:
|
2011-04-11 14:41:17 +02:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
.. code-block:: scala
|
|
|
|
|
|
|
|
|
|
|
|
class SpecificActor extends GenericActor {
|
|
|
|
|
|
def specificMessageHandler = {
|
|
|
|
|
|
case event: MyMsg => printf("specific: %s\n", event.subject)
|
|
|
|
|
|
}
|
2011-04-09 19:55:46 -06:00
|
|
|
|
}
|
2011-09-21 23:41:52 +02:00
|
|
|
|
|
2011-04-11 21:12:44 -06:00
|
|
|
|
case class MyMsg(subject: String)
|