2011-12-15 12:05:48 +01:00
|
|
|
|
.. _actor-systems:
|
2011-12-14 20:26:28 +01:00
|
|
|
|
|
|
|
|
|
|
Actor Systems
|
|
|
|
|
|
=============
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
Actors are objects which encapsulate state and behavior, they communicate
|
|
|
|
|
|
exclusively by exchanging messages which are placed into the recipient’s
|
2012-02-14 19:50:01 +07:00
|
|
|
|
mailbox. In a sense, actors are the most stringent form of object-oriented
|
2012-01-23 13:49:19 +01:00
|
|
|
|
programming, but it serves better to view them as persons: while modeling a
|
|
|
|
|
|
solution with actors, envision a group of people and assign sub-tasks to them,
|
|
|
|
|
|
arrange their functions into an organizational structure and think about how to
|
|
|
|
|
|
escalate failure (all with the benefit of not actually dealing with people,
|
|
|
|
|
|
which means that we need not concern ourselves with their emotional state or
|
|
|
|
|
|
moral issues). The result can then serve as a mental scaffolding for building
|
2011-12-14 20:26:28 +01:00
|
|
|
|
the software implementation.
|
|
|
|
|
|
|
2012-05-24 16:49:24 +02:00
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
|
|
|
|
An ActorSystem is a heavyweight structure that will allocate 1…N Threads,
|
|
|
|
|
|
so create one per logical application.
|
|
|
|
|
|
|
2011-12-14 20:26:28 +01:00
|
|
|
|
Hierarchical Structure
|
|
|
|
|
|
----------------------
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
Like in an economic organization, actors naturally form hierarchies. One actor,
|
|
|
|
|
|
which is to oversee a certain function in the program might want to split up
|
|
|
|
|
|
its task into smaller, more manageable pieces. For this purpose it starts child
|
|
|
|
|
|
actors which it supervises. While the details of supervision are explained
|
|
|
|
|
|
:ref:`here <supervision>`, we shall concentrate on the underlying concepts in
|
|
|
|
|
|
this section. The only prerequisite is to know that each actor has exactly one
|
2011-12-15 17:12:19 +01:00
|
|
|
|
supervisor, which is the actor that created it.
|
2011-12-14 20:26:28 +01:00
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
The quintessential feature of actor systems is that tasks are split up and
|
|
|
|
|
|
delegated until they become small enough to be handled in one piece. In doing
|
|
|
|
|
|
so, not only is the task itself clearly structured, but the resulting actors
|
|
|
|
|
|
can be reasoned about in terms of which messages they should process, how they
|
2012-04-24 07:59:12 +02:00
|
|
|
|
should react normally and how failure should be handled. If one actor does not
|
2012-01-23 13:49:19 +01:00
|
|
|
|
have the means for dealing with a certain situation, it sends a corresponding
|
|
|
|
|
|
failure message to its supervisor, asking for help. The recursive structure
|
|
|
|
|
|
then allows to handle failure at the right level.
|
|
|
|
|
|
|
|
|
|
|
|
Compare this to layered software design which easily devolves into defensive
|
|
|
|
|
|
programming with the aim of not leaking any failure out: if the problem is
|
|
|
|
|
|
communicated to the right person, a better solution can be found than if
|
2011-12-14 20:26:28 +01:00
|
|
|
|
trying to keep everything “under the carpet”.
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
Now, the difficulty in designing such a system is how to decide who should
|
|
|
|
|
|
supervise what. There is of course no single best solution, but there are a few
|
2012-04-24 07:59:12 +02:00
|
|
|
|
guidelines which might be helpful:
|
2011-12-14 20:26:28 +01:00
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
- If one actor manages the work another actor is doing, e.g. by passing on
|
|
|
|
|
|
sub-tasks, then the manager should supervise the child. The reason is that
|
|
|
|
|
|
the manager knows which kind of failures are expected and how to handle
|
2011-12-14 20:26:28 +01:00
|
|
|
|
them.
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
- If one actor carries very important data (i.e. its state shall not be lost
|
|
|
|
|
|
if avoidable), this actor should source out any possibly dangerous sub-tasks
|
|
|
|
|
|
to children it supervises and handle failures of these children as
|
|
|
|
|
|
appropriate. Depending on the nature of the requests, it may be best to
|
|
|
|
|
|
create a new child for each request, which simplifies state management for
|
|
|
|
|
|
collecting the replies. This is known as the “Error Kernel Pattern” from
|
2011-12-14 20:26:28 +01:00
|
|
|
|
Erlang.
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
- If one actor depends on another actor for carrying out its duty, it should
|
|
|
|
|
|
watch that other actor’s liveness and act upon receiving a termination
|
|
|
|
|
|
notice. This is different from supervision, as the watching party has no
|
|
|
|
|
|
influence on the supervisor strategy, and it should be noted that a
|
|
|
|
|
|
functional dependency alone is not a criterion for deciding where to place a
|
2011-12-14 20:26:28 +01:00
|
|
|
|
certain child actor in the hierarchy.
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
There are of course always exceptions to these rules, but no matter whether you
|
2011-12-14 20:26:28 +01:00
|
|
|
|
follow the rules or break them, you should always have a reason.
|
|
|
|
|
|
|
|
|
|
|
|
Configuration Container
|
|
|
|
|
|
-----------------------
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
The actor system as a collaborating ensemble of actors is the natural unit for
|
|
|
|
|
|
managing shared facilities like scheduling services, configuration, logging,
|
|
|
|
|
|
etc. Several actor systems with different configuration may co-exist within the
|
|
|
|
|
|
same JVM without problems, there is no global shared state within Akka itself.
|
|
|
|
|
|
Couple this with the transparent communication between actor systems—within one
|
|
|
|
|
|
node or across a network connection—to see that actor systems themselves can be
|
2011-12-14 20:26:28 +01:00
|
|
|
|
used as building blocks in a functional hierarchy.
|
|
|
|
|
|
|
|
|
|
|
|
Actor Best Practices
|
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
#. Actors should be like nice co-workers: do their job efficiently without
|
|
|
|
|
|
bothering everyone else needlessly and avoid hogging resources. Translated
|
|
|
|
|
|
to programming this means to process events and generate responses (or more
|
|
|
|
|
|
requests) in an event-driven manner. Actors should not block (i.e. passively
|
2012-10-15 21:34:31 +02:00
|
|
|
|
wait while occupying a Thread) on some external entity—which might be a
|
|
|
|
|
|
lock, a network socket, etc.—unless it is unavoidable; in the latter case
|
|
|
|
|
|
see below.
|
2011-12-14 20:26:28 +01:00
|
|
|
|
|
2011-12-15 17:12:19 +01:00
|
|
|
|
#. Do not pass mutable objects between actors. In order to ensure that, prefer
|
|
|
|
|
|
immutable messages. If the encapsulation of actors is broken by exposing
|
|
|
|
|
|
their mutable state to the outside, you are back in normal Java concurrency
|
|
|
|
|
|
land with all the drawbacks.
|
2011-12-14 20:26:28 +01:00
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
#. Actors are made to be containers for behavior and state, embracing this
|
|
|
|
|
|
means to not routinely send behavior within messages (which may be tempting
|
|
|
|
|
|
using Scala closures). One of the risks is to accidentally share mutable
|
|
|
|
|
|
state between actors, and this violation of the actor model unfortunately
|
|
|
|
|
|
breaks all the properties which make programming in actors such a nice
|
2011-12-14 20:26:28 +01:00
|
|
|
|
experience.
|
|
|
|
|
|
|
2012-04-23 12:25:33 +02:00
|
|
|
|
#. Top-level actors are the innermost part of your Error Kernel, so create them
|
|
|
|
|
|
sparingly and prefer truly hierarchical systems. This has benefits wrt.
|
|
|
|
|
|
fault-handling (both considering the granularity of configuration and the
|
|
|
|
|
|
performance) and it also reduces the number of blocking calls made, since
|
|
|
|
|
|
the creation of top-level actors involves synchronous messaging.
|
|
|
|
|
|
|
2012-10-15 21:34:31 +02:00
|
|
|
|
Blocking Needs Careful Management
|
|
|
|
|
|
---------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
In some cases it is unavoidable to do blocking operations, i.e. to put a thread
|
|
|
|
|
|
to sleep for an indeterminate time, waiting for an external event to occur.
|
|
|
|
|
|
Examples are legacy RDBMS drivers or messaging APIs, and the underlying reason
|
|
|
|
|
|
in typically that (network) I/O occurs under the covers. When facing this, you
|
|
|
|
|
|
may be tempted to just wrap the blocking call inside a :class:`Future` and work
|
|
|
|
|
|
with that instead, but this strategy is too simple: you are quite likely to
|
|
|
|
|
|
find bottle-necks or run out of memory or threads when the application runs
|
|
|
|
|
|
under increased load.
|
|
|
|
|
|
|
|
|
|
|
|
The non-exhaustive list of adequate solutions to the “blocking problem”
|
|
|
|
|
|
includes the following suggestions:
|
|
|
|
|
|
|
|
|
|
|
|
- Do the blocking call within an actor (or a set of actors managed by a router
|
|
|
|
|
|
[:ref:`Java <routing-java>`, :ref:`Scala <routing-scala>`]), making sure to
|
|
|
|
|
|
configure a thread pool which is either dedicated for this purpose or
|
|
|
|
|
|
sufficiently sized.
|
|
|
|
|
|
|
|
|
|
|
|
- Do the blocking call within a :class:`Future`, ensuring an upper bound on
|
|
|
|
|
|
the number of such calls at any point in time (submitting an unbounded
|
|
|
|
|
|
number of tasks of this nature will exhaust your memory or thread limits).
|
|
|
|
|
|
|
|
|
|
|
|
- Do the blocking call within a :class:`Future`, providing a thread pool with
|
|
|
|
|
|
an upper limit on the number of threads which is appropriate for the
|
|
|
|
|
|
hardware on which the application runs.
|
|
|
|
|
|
|
|
|
|
|
|
- Dedicate a single thread to manage a set of blocking resources (e.g. a NIO
|
|
|
|
|
|
selector driving multiple channels) and dispatch events as they occur as
|
|
|
|
|
|
actor messages.
|
|
|
|
|
|
|
|
|
|
|
|
The first possibility is especially well-suited for resources which are
|
|
|
|
|
|
single-threaded in nature, like database handles which traditionally can only
|
|
|
|
|
|
execute one outstanding query at a time and use internal synchronization to
|
|
|
|
|
|
ensure this. A common pattern is to create a router for N actors, each of which
|
|
|
|
|
|
wraps a single DB connection and handles queries as sent to the router. The
|
|
|
|
|
|
number N must then be tuned for maximum throughput, which will vary depending
|
|
|
|
|
|
on which DBMS is deployed on what hardware.
|
|
|
|
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
|
|
|
|
Configuring thread pools is a task best delegated to Akka, simply configure
|
|
|
|
|
|
in the ``application.conf`` and instantiate through an :class:`ActorSystem`
|
|
|
|
|
|
[:ref:`Java <dispatcher-lookup-java>`, :ref:`Scala
|
|
|
|
|
|
<dispatcher-lookup-scala>`]
|
|
|
|
|
|
|
2011-12-14 20:26:28 +01:00
|
|
|
|
What you should not concern yourself with
|
|
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
2012-01-23 13:49:19 +01:00
|
|
|
|
An actor system manages the resources it is configured to use in order to run
|
|
|
|
|
|
the actors which it contains. There may be millions of actors within one such
|
|
|
|
|
|
system, after all the mantra is to view them as abundant and they weigh in at
|
|
|
|
|
|
an overhead of only roughly 300 bytes per instance. Naturally, the exact order
|
|
|
|
|
|
in which messages are processed in large systems is not controllable by the
|
|
|
|
|
|
application author, but this is also not intended. Take a step back and relax
|
2011-12-14 20:26:28 +01:00
|
|
|
|
while Akka does the heavy lifting under the hood.
|
|
|
|
|
|
|