General concepts docs ported to typed (#27765)
This commit is contained in:
parent
ea74f905ea
commit
0719de035b
24 changed files with 814 additions and 624 deletions
|
|
@ -209,7 +209,7 @@ Java
|
||||||
|
|
||||||
Note that stopped entities will be started again when a new message is targeted to the entity.
|
Note that stopped entities will be started again when a new message is targeted to the entity.
|
||||||
|
|
||||||
If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see @ref:[Supervision](general/supervision.md#sharding)
|
If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see @ref:[Backoff supervisor and sharding](fault-tolerance.md#sharding)
|
||||||
|
|
||||||
## Graceful Shutdown
|
## Graceful Shutdown
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ by overriding the `logFailure` method.
|
||||||
## Supervision of Top-Level Actors
|
## Supervision of Top-Level Actors
|
||||||
|
|
||||||
Toplevel actors means those which are created using `system.actorOf()`, and
|
Toplevel actors means those which are created using `system.actorOf()`, and
|
||||||
they are children of the @ref:[User Guardian](general/supervision.md#user-guardian). There are no
|
they are children of the @ref:[User Guardian](supervision-classic.md#user-guardian). There are no
|
||||||
special rules applied in this case, the guardian applies the configured
|
special rules applied in this case, the guardian applies the configured
|
||||||
strategy.
|
strategy.
|
||||||
|
|
||||||
|
|
@ -230,3 +230,105 @@ Scala
|
||||||
|
|
||||||
Java
|
Java
|
||||||
: @@snip [FaultHandlingTest.java](/akka-docs/src/test/java/jdocs/actor/FaultHandlingTest.java) { #escalate-restart }
|
: @@snip [FaultHandlingTest.java](/akka-docs/src/test/java/jdocs/actor/FaultHandlingTest.java) { #escalate-restart }
|
||||||
|
|
||||||
|
|
||||||
|
## Delayed restarts for classic actors
|
||||||
|
|
||||||
|
The supervision strategy to restart a classic actor only provides immediate restart. In some cases that will only trigger
|
||||||
|
the same failure right away and giving things a bit of time before restarting is required to actually resolve the failure.
|
||||||
|
|
||||||
|
The `akka.pattern.BackoffSupervisor` implements the so-called
|
||||||
|
*exponential backoff supervision strategy*, starting a child actor again when it fails, each time with a growing time delay between restarts.
|
||||||
|
|
||||||
|
This pattern is useful when the started actor fails <a id="^1" href="#1">[1]</a> because some external resource is not available,
|
||||||
|
and we need to give it some time to start-up again. One of the prime examples when this is useful is
|
||||||
|
when a @ref:[PersistentActor](persistence.md) fails (by stopping) with a persistence failure - which indicates that
|
||||||
|
the database may be down or overloaded, in such situations it makes most sense to give it a little bit of time
|
||||||
|
to recover before the persistent actor is started.
|
||||||
|
|
||||||
|
> <a id="1" href="#^1">[1]</a> A failure can be indicated in two different ways; by an actor stopping or crashing.
|
||||||
|
|
||||||
|
### Supervision strategies
|
||||||
|
|
||||||
|
There are two basic supervision strategies available for backoff:
|
||||||
|
|
||||||
|
* 'On failure': The supervisor will terminate and then start the supervised actor if it crashes. If the supervised actor stops normally (e.g. through `context.stop`), the supervisor will be terminated and no further attempt to start the supervised actor will be done.
|
||||||
|
* 'On stop': The supervisor will terminate and then start the supervised actor if it terminates in any way (consider this for `PersistentActor` since they stop on persistence failures instead of crashing)
|
||||||
|
|
||||||
|
To note that this supervision strategy does not restart the actor but rather stops and starts it. Be aware of it if you
|
||||||
|
use @scala[`Stash` trait’s] @java[`AbstractActorWithStash`] in combination with the backoff supervision strategy.
|
||||||
|
The `preRestart` hook will not be executed if the supervised actor fails or stops and you will miss the opportunity
|
||||||
|
to unstash the messages.
|
||||||
|
|
||||||
|
### Sharding
|
||||||
|
If the 'on stop' strategy is used for sharded actors a final termination message should be configured and used to terminate the actor on passivation. Otherwise the supervisor will just stop and start the actor again.
|
||||||
|
|
||||||
|
The termination message is configured with:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded }
|
||||||
|
|
||||||
|
And must be used for passivation:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded-passivation }
|
||||||
|
|
||||||
|
|
||||||
|
### Simple backoff
|
||||||
|
|
||||||
|
The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has stopped
|
||||||
|
because of a failure, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-stop }
|
||||||
|
|
||||||
|
The above is equivalent to this Java code:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-imports }
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-stop }
|
||||||
|
|
||||||
|
Using a `randomFactor` to add a little bit of additional variance to the backoff intervals
|
||||||
|
is highly recommended, in order to avoid multiple actors re-start at the exact same point in time,
|
||||||
|
for example because they were stopped due to a shared resource such as a database going down
|
||||||
|
and re-starting after the same configured interval. By adding additional randomness to the
|
||||||
|
re-start intervals the actors will start in slightly different points in time, thus avoiding
|
||||||
|
large spikes of traffic hitting the recovering shared database or other resource that they all need to contact.
|
||||||
|
|
||||||
|
The `akka.pattern.BackoffSupervisor` actor can also be configured to stop and start the actor after a delay when the actor
|
||||||
|
crashes and the supervision strategy decides that it should restart.
|
||||||
|
|
||||||
|
The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has crashed
|
||||||
|
because of some exception, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-fail }
|
||||||
|
|
||||||
|
The above is equivalent to this Java code:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-imports }
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-fail }
|
||||||
|
|
||||||
|
### Customization
|
||||||
|
|
||||||
|
The `akka.pattern.BackoffOnFailureOptions` and `akka.pattern.BackoffOnRestartOptions` can be used to customize the behavior of the back-off supervisor actor.
|
||||||
|
Options are:
|
||||||
|
* `withAutoReset`: The backoff is reset if no failure/stop occurs within the duration. This is the default behaviour with `minBackoff` as default value
|
||||||
|
* `withManualReset`: The child must send `BackoffSupervisor.Reset` to its backoff supervisor (parent)
|
||||||
|
* `withSupervisionStrategy`: Sets a custom `OneForOneStrategy` (as each backoff supervisor only has one child). The default strategy uses the `akka.actor.SupervisorStrategy.defaultDecider` which stops and starts the child on exceptions.
|
||||||
|
* `withMaxNrOfRetries`: Sets the maximum number of retries until the supervisor will give up (`-1` is default which means no limit of retries). Note: This is set on the supervision strategy, so setting a different strategy resets the `maxNrOfRetries`.
|
||||||
|
* `withReplyWhileStopped`: By default all messages received while the child is stopped are forwarded to dead letters. With this set, the supervisor will reply to the sender instead.
|
||||||
|
|
||||||
|
Only available on `BackoffOnStopOptions`:
|
||||||
|
* `withDefaultStoppingStrategy`: Sets a `OneForOneStrategy` with the stopping decider that stops the child on all exceptions.
|
||||||
|
* `withFinalStopMessage`: Allows to define a predicate to decide on finally stopping the child (and supervisor). Used for passivate sharded actors - see above.
|
||||||
|
|
||||||
|
Some examples:
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-stop }
|
||||||
|
|
||||||
|
The above code sets up a back-off supervisor that requires the child actor to send a `akka.pattern.BackoffSupervisor.Reset` message
|
||||||
|
to its parent when a message is successfully processed, resetting the back-off. It also uses a default stopping strategy, any exception
|
||||||
|
will cause the child to stop.
|
||||||
|
|
||||||
|
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-fail }
|
||||||
|
|
||||||
|
The above code sets up a back-off supervisor that stops and starts the child after back-off if MyException is thrown, any other exception will be
|
||||||
|
escalated. The back-off is automatically reset if the child does not throw any errors within 10 seconds.
|
||||||
|
|
|
||||||
|
|
@ -23,84 +23,73 @@ so create one per logical application.
|
||||||
Like in an economic organization, actors naturally form hierarchies. One actor,
|
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
|
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
|
its task into smaller, more manageable pieces. For this purpose, it starts child
|
||||||
actors which it supervises. While the details of supervision are explained
|
actors.
|
||||||
@ref:[here](supervision.md), we shall concentrate on the underlying concepts in
|
|
||||||
this section. The only prerequisite is to know that each actor has exactly one
|
|
||||||
supervisor, which is the actor that created it.
|
|
||||||
|
|
||||||
The quintessential feature of actor systems is that tasks are split up and
|
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
|
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
|
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
|
can be reasoned about in terms of which messages they should process, how they
|
||||||
should react normally and how failure should be handled. If one actor does not
|
should react normally and how failure should be handled.
|
||||||
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
|
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
|
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
|
communicated to the right person, a better solution can be found than if
|
||||||
trying to keep everything “under the carpet”.
|
trying to keep everything “under the carpet”.
|
||||||
|
|
||||||
Now, the difficulty in designing such a system is how to decide who should
|
Now, the difficulty in designing such a system is how to decide how to
|
||||||
supervise what. There is no single best solution, but there are a few
|
structure the work. There is no single best solution, but there are a few
|
||||||
guidelines which might be helpful:
|
guidelines which might be helpful:
|
||||||
|
|
||||||
* 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
|
|
||||||
them.
|
|
||||||
* If one actor carries very important data (i.e. its state shall not be lost
|
* 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
|
if avoidable), this actor should source out any possibly dangerous sub-tasks
|
||||||
to children it supervises and handle failures of these children as
|
to children and handle failures of these children as appropriate. Depending on
|
||||||
appropriate. Depending on the nature of the requests, it may be best to
|
the nature of the requests, it may be best to create a new child for each request,
|
||||||
create a new child for each request, which simplifies state management for
|
which simplifies state management for collecting the replies. This is known as the
|
||||||
collecting the replies. This is known as the “Error Kernel Pattern” from
|
“Error Kernel Pattern” from Erlang.
|
||||||
Erlang.
|
|
||||||
* If one actor depends on another actor for carrying out its duty, it should
|
* 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
|
watch that other actor’s liveness and act upon receiving a termination
|
||||||
notice. This is different from supervision, as the watching party has no
|
notice.
|
||||||
influence on the supervisor strategy, and it should be noted that a
|
* If one actor has multiple responsibilities each responsibility can often be pushed
|
||||||
functional dependency alone is not a criterion for deciding where to place a
|
into a separate child to make the logic and state more simple.
|
||||||
certain child actor in the hierarchy.
|
|
||||||
|
|
||||||
There are always exceptions to these rules, but no matter whether you
|
|
||||||
follow the rules or break them, you should always have a reason.
|
|
||||||
|
|
||||||
## Configuration Container
|
## Configuration Container
|
||||||
|
|
||||||
The actor system as a collaborating ensemble of actors is the natural unit for
|
The actor system as a collaborating ensemble of actors is the natural unit for
|
||||||
managing shared facilities like scheduling services, configuration, logging,
|
managing shared facilities like scheduling services, configuration, logging,
|
||||||
etc. Several actor systems with different configurations may co-exist within the
|
etc. Several actor systems with different configurations may co-exist within the
|
||||||
same JVM without problems, there is no global shared state within Akka itself.
|
same JVM without problems, there is no global shared state within Akka itself,
|
||||||
Couple this with the transparent communication between actor systems—within one
|
however the most common scenario will only involve a single actor system per JVM.
|
||||||
node or across a network connection—to see that actor systems themselves can be
|
|
||||||
used as building blocks in a functional hierarchy.
|
Couple this with the transparent communication between actor systems — within one
|
||||||
|
node or across a network connection — and actor systems are a perfect fit to form
|
||||||
|
a distributed application.
|
||||||
|
|
||||||
## Actor Best Practices
|
## Actor Best Practices
|
||||||
|
|
||||||
1. Actors should be like nice co-workers: do their job efficiently without
|
1. Actors should be like nice co-workers: do their job efficiently without
|
||||||
bothering everyone else needlessly and avoid hogging resources. Translated
|
bothering everyone else needlessly and avoid hogging resources. Translated
|
||||||
to programming this means to process events and generate responses (or more
|
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
|
requests) in an event-driven manner. Actors should not block (i.e. passively
|
||||||
wait while occupying a Thread) on some external entity—which might be a
|
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
|
lock, a network socket, etc.—unless it is unavoidable; in the latter case
|
||||||
see below.
|
see below.
|
||||||
2. Do not pass mutable objects between actors. In order to ensure that, prefer
|
2. Do not pass mutable objects between actors. In order to ensure that, prefer
|
||||||
immutable messages. If the encapsulation of actors is broken by exposing
|
immutable messages. If the encapsulation of actors is broken by exposing
|
||||||
their mutable state to the outside, you are back in normal Java concurrency
|
their mutable state to the outside, you are back in normal Java concurrency
|
||||||
land with all the drawbacks.
|
land with all the drawbacks.
|
||||||
3. Actors are made to be containers for behavior and state, embracing this
|
3. Actors are made to be containers for behavior and state, embracing this
|
||||||
means to not routinely send behavior within messages (which may be tempting
|
means to not routinely send behavior within messages (which may be tempting
|
||||||
using Scala closures). One of the risks is to accidentally share mutable
|
using Scala closures). One of the risks is to accidentally share mutable
|
||||||
state between actors, and this violation of the actor model unfortunately
|
state between actors, and this violation of the actor model unfortunately
|
||||||
breaks all the properties which make programming in actors such a nice
|
breaks all the properties which make programming in actors such a nice
|
||||||
experience.
|
experience.
|
||||||
4. Top-level actors are the innermost part of your Error Kernel, so create them
|
4. The top-level actor of the actor system is the innermost part of your
|
||||||
sparingly and prefer truly hierarchical systems. This has benefits with
|
Error Kernel, it should only be responsible for starting the various
|
||||||
respect to fault-handling (both considering the granularity of configuration
|
sub systems of your application, and not contain much logic in itself,
|
||||||
and the performance) and it also reduces the strain on the guardian actor,
|
prefer truly hierarchical systems. This has benefits with
|
||||||
which is a single point of contention if over-used.
|
respect to fault-handling (both considering the granularity of configuration
|
||||||
|
and the performance) and it also reduces the strain on the guardian actor,
|
||||||
|
which is a single point of contention if over-used.
|
||||||
|
|
||||||
## What you should not concern yourself with
|
## What you should not concern yourself with
|
||||||
|
|
||||||
|
|
@ -114,10 +103,9 @@ while Akka does the heavy lifting under the hood.
|
||||||
|
|
||||||
## Terminating ActorSystem
|
## Terminating ActorSystem
|
||||||
|
|
||||||
When you know everything is done for your application, you can call the
|
When you know everything is done for your application, you can have the user guardian
|
||||||
`terminate` method of `ActorSystem`. That will run @ref:[`CoordinatedShutdown`](../actors.md#coordinated-shutdown)
|
actor stop, or call the `terminate` method of `ActorSystem`. That will run @ref:[`CoordinatedShutdown`](../actors.md#coordinated-shutdown)
|
||||||
followed by stopping the guardian actor, which in turn will recursively stop all its child actors,
|
stopping all running actors.
|
||||||
and finally the system guardian.
|
|
||||||
|
|
||||||
If you want to execute some operations while terminating `ActorSystem`,
|
If you want to execute some operations while terminating `ActorSystem`,
|
||||||
look at @ref:[`CoordinatedShutdown`](../actors.md#coordinated-shutdown).
|
look at @ref:[`CoordinatedShutdown`](../actors.md#coordinated-shutdown).
|
||||||
|
|
|
||||||
|
|
@ -158,12 +158,9 @@ their parent.
|
||||||
|
|
||||||
## Supervisor Strategy
|
## Supervisor Strategy
|
||||||
|
|
||||||
The final piece of an actor is its strategy for handling faults of its
|
The final piece of an actor is its a strategy for handling unexpected exceptions - failures.
|
||||||
children. Fault handling is then done transparently by Akka, applying one
|
Fault handling is then done transparently by Akka, applying one of the strategies described
|
||||||
of the strategies described in @ref:[Fault Tolerance](../typed/fault-tolerance.md)
|
in @ref:[Fault Tolerance](../typed/fault-tolerance.md) for each failure.
|
||||||
for each incoming failure.
|
|
||||||
As this strategy is fundamental to how an actor system is structured, it
|
|
||||||
cannot be changed once an actor has been created.
|
|
||||||
|
|
||||||
## When an Actor Terminates
|
## When an Actor Terminates
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
# Actor References, Paths and Addresses
|
# Actor References, Paths and Addresses
|
||||||
|
|
||||||
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 Akka application.
|
||||||
@ref:[Actor Systems](actor-systems.md) form intrinsic supervision hierarchies as well as that
|
|
||||||
communication between actors is transparent with respect to their placement
|
|
||||||
across multiple network nodes.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The above image displays the relationship between the most important entities
|
The above image displays the relationship between the most important entities
|
||||||
within an actor system, please read on for the details.
|
within an actor system, please read on for the details.
|
||||||
|
|
@ -15,11 +12,8 @@ within an actor system, please read on for the details.
|
||||||
|
|
||||||
An actor reference is a subtype of `ActorRef`, whose foremost purpose is
|
An actor reference is a subtype of `ActorRef`, whose foremost purpose is
|
||||||
to support sending messages to the actor it represents. Each actor has access
|
to support sending messages to the actor it represents. Each actor has access
|
||||||
to its canonical (local) reference through the `self` field; this
|
to its canonical (local) reference through the `ActorContext.self` field; this
|
||||||
reference is also included as sender reference by default for all messages sent
|
reference can be included in messages to other actors to get replies back.
|
||||||
to other actors. Conversely, during message processing the actor has access to
|
|
||||||
a reference representing the sender of the current message through the
|
|
||||||
`sender()` method.
|
|
||||||
|
|
||||||
There are several different types of actor references that are supported
|
There are several different types of actor references that are supported
|
||||||
depending on the configuration of the actor system:
|
depending on the configuration of the actor system:
|
||||||
|
|
@ -32,10 +26,6 @@ which support networking functions for those references which represent
|
||||||
actors within the same JVM. In order to also be reachable when sent to
|
actors within the same JVM. In order to also be reachable when sent to
|
||||||
other network nodes, these references include protocol and remote addressing
|
other network nodes, these references include protocol and remote addressing
|
||||||
information.
|
information.
|
||||||
* There is a subtype of local actor references which is used for routers (i.e.
|
|
||||||
actors mixing in the `Router` trait). Its logical structure is the
|
|
||||||
same as for the aforementioned local references, but sending a message to
|
|
||||||
them dispatches to one of their children directly instead.
|
|
||||||
* Remote actor references represent actors which are reachable using remote
|
* Remote actor references represent actors which are reachable using remote
|
||||||
communication, i.e. sending messages to them will serialize the messages
|
communication, i.e. sending messages to them will serialize the messages
|
||||||
transparently and send them to the remote JVM.
|
transparently and send them to the remote JVM.
|
||||||
|
|
@ -110,125 +100,28 @@ the creation ancestry of an actor, so it is completely deterministic as soon as
|
||||||
the actor system’s remoting configuration (and with it the address component of
|
the actor system’s remoting configuration (and with it the address component of
|
||||||
the path) is set.
|
the path) is set.
|
||||||
|
|
||||||
### Physical Actor Paths
|
|
||||||
|
|
||||||
While the logical actor path describes the functional location within one actor
|
|
||||||
system, configuration-based remote deployment means that an actor may be
|
|
||||||
created on a different network host than its parent, i.e. within a different
|
|
||||||
actor system. In this case, following the actor path from the root guardian up
|
|
||||||
entails traversing the network, which is a costly operation. Therefore, each
|
|
||||||
actor also has a physical path, starting at the root guardian of the actor
|
|
||||||
system where the actual actor object resides. Using this path as sender
|
|
||||||
reference when querying other actors will let them reply directly to this
|
|
||||||
actor, minimizing delays incurred by routing.
|
|
||||||
|
|
||||||
One important aspect is that a physical actor path never spans multiple actor
|
|
||||||
systems or JVMs. This means that the logical path (supervision hierarchy) and
|
|
||||||
the physical path (actor deployment) of an actor may diverge if one of its
|
|
||||||
ancestors is remotely supervised.
|
|
||||||
|
|
||||||
### Actor path alias or symbolic link?
|
### Actor path alias or symbolic link?
|
||||||
|
|
||||||
As in some real file-systems you might think of a “path alias” or “symbolic link” for an actor,
|
As in some real file-systems you might think of a “path alias” or “symbolic link” for an actor,
|
||||||
i.e. one actor may be reachable using more than one path.
|
i.e. one actor may be reachable using more than one path.
|
||||||
However, you should note that actor hierarchy is different from file system hierarchy.
|
However, you should note that actor hierarchy is different from file system hierarchy.
|
||||||
You cannot freely create actor paths like symbolic links to refer to arbitrary actors.
|
You cannot freely create actor paths like symbolic links to refer to arbitrary actors.
|
||||||
As described in the above logical and physical actor path sections,
|
|
||||||
an actor path must be either logical path which represents supervision hierarchy, or
|
|
||||||
physical path which represents actor deployment.
|
|
||||||
|
|
||||||
## How are Actor References obtained?
|
## How are Actor References obtained?
|
||||||
|
|
||||||
There are two general categories to how actor references may be obtained: by
|
There are two general categories to how actor references may be obtained: by
|
||||||
creating actors or by looking them up, where the latter functionality comes in
|
creating actors or by looking them up through the @ref:[Receptionist](../typed/actor-discovery.md#receptionist).
|
||||||
the two flavours of creating actor references from concrete actor paths and
|
|
||||||
querying the logical actor hierarchy.
|
|
||||||
|
|
||||||
### Creating Actors
|
### Creating Actors
|
||||||
|
|
||||||
An actor system is typically started by creating actors beneath the guardian
|
An actor system is started by creating actors beneath the user guardian
|
||||||
actor using the `ActorSystem.actorOf` method and then using
|
actor using the `ActorContext.spawn` method and then using
|
||||||
`ActorContext.actorOf` from within the created actors to spawn the actor
|
`ActorContext.spawn` from within the created actors to spawn the actor
|
||||||
tree. These methods return a reference to the newly created actor. Each actor
|
tree. These methods return a reference to the newly created actor. Each actor
|
||||||
has direct access (through its `ActorContext`) to references for its parent,
|
has direct access (through its `ActorContext`) to references for its parent,
|
||||||
itself and its children. These references may be sent within messages to other actors,
|
itself and its children. These references may be sent within messages to other actors,
|
||||||
enabling those to reply directly.
|
enabling those to reply directly.
|
||||||
|
|
||||||
### Looking up Actors by Concrete Path
|
|
||||||
|
|
||||||
In addition, actor references may be looked up using the
|
|
||||||
`ActorSystem.actorSelection` method. The selection can be used for
|
|
||||||
communicating with said actor and the actor corresponding to the selection
|
|
||||||
is looked up when delivering each message.
|
|
||||||
|
|
||||||
To acquire an `ActorRef` that is bound to the life-cycle of a specific actor
|
|
||||||
you need to send a message, such as the built-in `Identify` message, to the actor
|
|
||||||
and use the `sender()` reference of a reply from the actor.
|
|
||||||
|
|
||||||
#### Absolute vs. Relative Paths
|
|
||||||
|
|
||||||
In addition to `ActorSystem.actorSelection` there is also
|
|
||||||
`ActorContext.actorSelection`, which is available inside any actor as
|
|
||||||
`context.actorSelection`. This yields an actor selection much like its twin on
|
|
||||||
`ActorSystem`, but instead of looking up the path starting from the root
|
|
||||||
of the actor tree it starts out on the current actor. Path elements consisting
|
|
||||||
of two dots (`".."`) may be used to access the parent actor. You can for
|
|
||||||
example send a message to a specific sibling:
|
|
||||||
|
|
||||||
```
|
|
||||||
context.actorSelection("../brother") ! msg
|
|
||||||
```
|
|
||||||
|
|
||||||
Absolute paths may also be looked up on *context* in the usual way, i.e.
|
|
||||||
|
|
||||||
```scala
|
|
||||||
context.actorSelection("/user/serviceA") ! msg
|
|
||||||
```
|
|
||||||
|
|
||||||
will work as expected.
|
|
||||||
|
|
||||||
### Querying the Logical Actor Hierarchy
|
|
||||||
|
|
||||||
Since the actor system forms a file-system like hierarchy, matching on paths is
|
|
||||||
possible in the same way as supported by Unix shells: you may replace (parts
|
|
||||||
of) path element names with wildcards (*«*»* and *«?»*) to formulate a
|
|
||||||
selection which may match zero or more actual actors. Because the result is not
|
|
||||||
a single actor reference, it has a different type `ActorSelection` and
|
|
||||||
does not support the full set of operations an `ActorRef` does.
|
|
||||||
Selections may be formulated using the `ActorSystem.actorSelection` and
|
|
||||||
`ActorContext.actorSelection` methods and do support sending messages:
|
|
||||||
|
|
||||||
```
|
|
||||||
context.actorSelection("../*") ! msg
|
|
||||||
```
|
|
||||||
|
|
||||||
will send *msg* to all siblings including the current actor. As for references
|
|
||||||
obtained using *actorSelection*, a traversal of the supervision hierarchy is done in
|
|
||||||
order to perform the message send. As the exact set of actors which match a
|
|
||||||
selection may change even while a message is making its way to the recipients,
|
|
||||||
it is not possible to watch a selection for liveliness changes. In order to do
|
|
||||||
that, resolve the uncertainty by sending a request and gathering all answers,
|
|
||||||
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.
|
|
||||||
|
|
||||||
<a id="actorof-vs-actorselection"></a>
|
|
||||||
### Summary: `actorOf` vs. `actorSelection`
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
What the above sections described in some detail can be summarized and
|
|
||||||
memorized 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).
|
|
||||||
* `actorSelection` only ever looks up existing actors when messages are
|
|
||||||
delivered, i.e. does not create actors, or verify existence of actors
|
|
||||||
when the selection is created.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
## Actor Reference and Path Equality
|
## Actor Reference and Path Equality
|
||||||
|
|
||||||
Equality of `ActorRef` match the intention that an `ActorRef` corresponds to
|
Equality of `ActorRef` match the intention that an `ActorRef` corresponds to
|
||||||
|
|
@ -248,40 +141,6 @@ of the target actor is not taken into account when comparing actor paths.
|
||||||
When an actor is terminated, its reference will point to the dead letter mailbox,
|
When an actor is terminated, its reference will point to the dead letter mailbox,
|
||||||
DeathWatch will publish its final transition and in general it is not expected
|
DeathWatch will publish its final transition and in general it is not expected
|
||||||
to come back to life again (since the actor life cycle does not allow this).
|
to come back to life again (since the actor life cycle does not allow this).
|
||||||
While it is possible to create an actor at a later time with an identical
|
|
||||||
path—due to it being impossible to enforce the opposite without keeping
|
|
||||||
the set of all actors ever created available—this is not good practice:
|
|
||||||
messages sent with `actorSelection` to an actor which “died” suddenly start to work
|
|
||||||
again, but without any guarantee of ordering between this transition and any
|
|
||||||
other event, hence the new inhabitant of the path may receive messages which were destined for the
|
|
||||||
previous tenant.
|
|
||||||
|
|
||||||
It may be the right thing to do in very specific circumstances, but make sure
|
|
||||||
to confine the handling of this precisely to the actor’s supervisor, because
|
|
||||||
that is the only actor which can reliably detect proper deregistration of the
|
|
||||||
name, before which creation of the new child will fail.
|
|
||||||
|
|
||||||
It may also be required during testing, when the test subject depends on being
|
|
||||||
instantiated at a specific path. In that case it is best to mock its supervisor
|
|
||||||
so that it will forward the Terminated message to the appropriate point in the
|
|
||||||
test procedure, enabling the latter to await proper deregistration of the name.
|
|
||||||
|
|
||||||
## The Interplay with Remote Deployment
|
|
||||||
|
|
||||||
When an actor creates a child, the actor system’s deployer will decide whether
|
|
||||||
the new actor resides in the same JVM or on another node. In the second case,
|
|
||||||
creation of the actor will be triggered via a network connection to happen in a
|
|
||||||
different JVM and consequently within a different actor system. The remote
|
|
||||||
system will place the new actor below a special path reserved for this purpose
|
|
||||||
and the supervisor of the new actor will be a remote actor reference
|
|
||||||
(representing that actor which triggered its creation). In this case,
|
|
||||||
`context.parent` (the supervisor reference) and
|
|
||||||
`context.path.parent` (the parent node in the actor’s path) do not
|
|
||||||
represent the same actor. However, looking up the child’s name within the
|
|
||||||
supervisor will find it on the remote node, preserving logical structure e.g.
|
|
||||||
when sending to an unresolved actor reference.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## What is the Address part used for?
|
## What is the Address part used for?
|
||||||
|
|
||||||
|
|
@ -316,11 +175,4 @@ supervisors are remote actor references
|
||||||
|
|
||||||
The need to structure the name space for actors like this arises from a central
|
The need to structure the name space for actors like this arises from a central
|
||||||
and very simple design goal: everything in the hierarchy is an actor, and all
|
and very simple design goal: everything in the hierarchy is an actor, and all
|
||||||
actors function in the same way. Hence you can not only look up the actors you
|
actors function in the same way.
|
||||||
created, you can also look up the system guardian and send it a message (which
|
|
||||||
it will dutifully discard in this case). This powerful principle means that
|
|
||||||
there are no quirks to remember, it makes the whole system more uniform and
|
|
||||||
consistent.
|
|
||||||
|
|
||||||
If you want to read more about the top-level structure of an actor system, have
|
|
||||||
a look at @ref:[The Top-Level Supervisors](supervision.md#toplevel-supervisors).
|
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,15 @@ are provided. Later on you might need to amend the settings to change the defaul
|
||||||
or adapt for specific runtime environments. Typical examples of settings that you
|
or adapt for specific runtime environments. Typical examples of settings that you
|
||||||
might amend:
|
might amend:
|
||||||
|
|
||||||
* log level and logger backend
|
* log level and logger backend for the Akka internals
|
||||||
* enable remoting
|
* enable remoting
|
||||||
* message serializers
|
* message serializers
|
||||||
* definition of routers
|
|
||||||
* tuning of dispatchers
|
* tuning of dispatchers
|
||||||
|
|
||||||
Akka uses the [Typesafe Config Library](https://github.com/typesafehub/config), which might also be a good choice
|
Akka uses the [Typesafe Config Library](https://github.com/lightbend/config), which might also be a good choice
|
||||||
for the configuration of your own application or library built with or without
|
for the configuration of your own application or library built with or without
|
||||||
Akka. This library is implemented in Java with no external dependencies; you
|
Akka. This library is implemented in Java with no external dependencies;
|
||||||
should have a look at its documentation (in particular about [ConfigFactory](https://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigFactory.html)),
|
This is only a summary of the most important parts for more details see [the config library docs](https://github.com/lightbend/config/blob/master/README.md).
|
||||||
which is only summarized in the following.
|
|
||||||
|
|
||||||
@@@ warning
|
|
||||||
|
|
||||||
If you use Akka from the Scala REPL from the 2.9.x series,
|
|
||||||
and you do not provide your own ClassLoader to the ActorSystem,
|
|
||||||
start the REPL with "-Yrepl-sync" to work around a deficiency in
|
|
||||||
the REPLs provided Context ClassLoader.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
## Where configuration is read from
|
## Where configuration is read from
|
||||||
|
|
||||||
|
|
@ -88,6 +77,9 @@ A custom `application.conf` might look like this:
|
||||||
|
|
||||||
akka {
|
akka {
|
||||||
|
|
||||||
|
# Logger config for Akka internals and classic actors, the new API relies
|
||||||
|
# directly on SLF4J and your config for the logger backend.
|
||||||
|
|
||||||
# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs
|
# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs
|
||||||
# to STDOUT)
|
# to STDOUT)
|
||||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||||
|
|
@ -149,12 +141,13 @@ If the system or config property `akka.log-config-on-start` is set to `on`, then
|
||||||
complete configuration is logged at INFO level when the actor system is started. This is
|
complete configuration is logged at INFO level when the actor system is started. This is
|
||||||
useful when you are uncertain of what configuration is used.
|
useful when you are uncertain of what configuration is used.
|
||||||
|
|
||||||
|
@@@div { .group-scala }
|
||||||
|
|
||||||
If in doubt, you can inspect your configuration objects
|
If in doubt, you can inspect your configuration objects
|
||||||
before or after using them to construct an actor system:
|
before or after using them to construct an actor system:
|
||||||
|
|
||||||
@@@vars
|
|
||||||
```
|
```
|
||||||
Welcome to Scala $scala.binary_version$ (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0).
|
Welcome to Scala 2.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0).
|
||||||
Type in expressions to have them evaluated.
|
Type in expressions to have them evaluated.
|
||||||
Type :help for more information.
|
Type :help for more information.
|
||||||
|
|
||||||
|
|
@ -174,6 +167,7 @@ res1: java.lang.String =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
The comments preceding every item give detailed information about the origin of
|
The comments preceding every item give detailed information about the origin of
|
||||||
|
|
@ -181,11 +175,12 @@ the setting (file & line number) plus possible comments which were present,
|
||||||
e.g. in the reference configuration. The settings as merged with the reference
|
e.g. in the reference configuration. The settings as merged with the reference
|
||||||
and parsed by the actor system can be displayed like this:
|
and parsed by the actor system can be displayed like this:
|
||||||
|
|
||||||
```java
|
Scala
|
||||||
final ActorSystem system = ActorSystem.create();
|
: @@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #dump-config }
|
||||||
System.out.println(system.settings());
|
|
||||||
// this is a shortcut for system.settings().config().root().render()
|
Java
|
||||||
```
|
: @@snip [ConfigDocTest.java](/akka-docs/src/test/java/jdocs/config/ConfigDocTest.java) { #dump-config }
|
||||||
|
|
||||||
|
|
||||||
## A Word About ClassLoaders
|
## A Word About ClassLoaders
|
||||||
|
|
||||||
|
|
@ -203,7 +198,7 @@ This implies that putting Akka on the boot class path will yield
|
||||||
## Application specific settings
|
## Application specific settings
|
||||||
|
|
||||||
The configuration can also be used for application specific settings.
|
The configuration can also be used for application specific settings.
|
||||||
A good practice is to place those settings in an @ref:[Extension](../extending-akka.md#extending-akka-settings).
|
A good practice is to place those settings in an @ref:[Extension](../extending-akka.md#extending-akka-settings).
|
||||||
|
|
||||||
## Configuring multiple ActorSystem
|
## Configuring multiple ActorSystem
|
||||||
|
|
||||||
|
|
@ -229,12 +224,11 @@ my.own.setting = 42
|
||||||
my.other.setting = "hello"
|
my.other.setting = "hello"
|
||||||
```
|
```
|
||||||
|
|
||||||
```scala
|
Scala
|
||||||
val config = ConfigFactory.load()
|
: @@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #separate-apps }
|
||||||
val app1 = ActorSystem("MyApp1", config.getConfig("myapp1").withFallback(config))
|
|
||||||
val app2 = ActorSystem("MyApp2",
|
Java
|
||||||
config.getConfig("myapp2").withOnlyPath("akka").withFallback(config))
|
: @@snip [ConfigDocTest.java](/akka-docs/src/test/java/jdocs/config/ConfigDocTest.java) { #separate-apps }
|
||||||
```
|
|
||||||
|
|
||||||
These two samples demonstrate different variations of the “lift-a-subtree”
|
These two samples demonstrate different variations of the “lift-a-subtree”
|
||||||
trick: in the first case, the configuration accessible from within the actor
|
trick: in the first case, the configuration accessible from within the actor
|
||||||
|
|
@ -270,7 +264,13 @@ substitutions.
|
||||||
You may also specify and parse the configuration programmatically in other ways when instantiating
|
You may also specify and parse the configuration programmatically in other ways when instantiating
|
||||||
the `ActorSystem`.
|
the `ActorSystem`.
|
||||||
|
|
||||||
@@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #imports #custom-config }
|
|
||||||
|
Scala
|
||||||
|
: @@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #imports #custom-config }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ConfigDocTest.java](/akka-docs/src/test/java/jdocs/config/ConfigDocTest.java) { #imports #custom-config }
|
||||||
|
|
||||||
|
|
||||||
## Reading configuration from a custom location
|
## Reading configuration from a custom location
|
||||||
|
|
||||||
|
|
@ -313,7 +313,12 @@ you could put a config string in code using
|
||||||
You can also combine your custom config with the usual config,
|
You can also combine your custom config with the usual config,
|
||||||
that might look like:
|
that might look like:
|
||||||
|
|
||||||
@@snip [ConfigDoc.java](/akka-docs/src/test/java/jdocs/config/ConfigDoc.java) { #java-custom-config }
|
Scala
|
||||||
|
: @@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #custom-config-2 }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [ConfigDocTest.java](/akka-docs/src/test/java/jdocs/config/ConfigDocTest.java) { #custom-config-2 }
|
||||||
|
|
||||||
|
|
||||||
When working with `Config` objects, keep in mind that there are
|
When working with `Config` objects, keep in mind that there are
|
||||||
three "layers" in the cake:
|
three "layers" in the cake:
|
||||||
|
|
@ -340,41 +345,6 @@ Includes at the top of `application.conf` will be overridden by
|
||||||
the rest of `application.conf`, while those at the bottom will
|
the rest of `application.conf`, while those at the bottom will
|
||||||
override the earlier stuff.
|
override the earlier stuff.
|
||||||
|
|
||||||
## Actor Deployment Configuration
|
|
||||||
|
|
||||||
Deployment settings for specific actors can be defined in the `akka.actor.deployment`
|
|
||||||
section of the configuration. In the deployment section it is possible to define
|
|
||||||
things like dispatcher, mailbox, router settings, and remote deployment.
|
|
||||||
Configuration of these features are described in the chapters detailing corresponding
|
|
||||||
topics. An example may look like this:
|
|
||||||
|
|
||||||
@@snip [ConfigDocSpec.scala](/akka-docs/src/test/scala/docs/config/ConfigDocSpec.scala) { #deployment-section }
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
The deployment section for a specific actor is identified by the
|
|
||||||
path of the actor relative to `/user`.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
You can use asterisks as wildcard matches for the actor path sections, so you could specify:
|
|
||||||
`/*/sampleActor` and that would match all `sampleActor` on that level in the hierarchy.
|
|
||||||
In addition, please note:
|
|
||||||
|
|
||||||
* you can also use wildcards in the last position to match all actors at a certain level: `/someParent/*`
|
|
||||||
* you can use double-wildcards in the last position to match all child actors and their children
|
|
||||||
recursively: `/someParent/**`
|
|
||||||
* non-wildcard matches always have higher priority to match than wildcards, and single wildcard matches
|
|
||||||
have higher priority than double-wildcards, so: `/foo/bar` is considered **more specific** than
|
|
||||||
`/foo/*`, which is considered **more specific** than `/foo/**`. Only the highest priority match is used
|
|
||||||
* wildcards **cannot** be used to partially match section, like this: `/foo*/bar`, `/f*o/bar` etc.
|
|
||||||
|
|
||||||
@@@ note
|
|
||||||
|
|
||||||
Double-wildcards can only be placed in the last position.
|
|
||||||
|
|
||||||
@@@
|
|
||||||
|
|
||||||
## Listing of the Reference Configuration
|
## Listing of the Reference Configuration
|
||||||
|
|
||||||
Each Akka module has a reference configuration file with the default values.
|
Each Akka module has a reference configuration file with the default values.
|
||||||
|
|
@ -384,31 +354,61 @@ Each Akka module has a reference configuration file with the default values.
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-actor/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-actor/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-actor-typed"></a>
|
||||||
|
### akka-actor-typed
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-actor-typed/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-cluster-typed"></a>
|
||||||
|
### akka-cluster-typed
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-cluster-typed/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-akka-cluster"></a>
|
<a id="config-akka-cluster"></a>
|
||||||
### akka-cluster
|
### akka-cluster
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-cluster/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-cluster/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-discovery"></a>
|
||||||
|
### akka-discovery
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-discovery/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-coordination"></a>
|
||||||
|
### akka-coordination
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-coordination/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-akka-multi-node-testkit"></a>
|
<a id="config-akka-multi-node-testkit"></a>
|
||||||
### akka-multi-node-testkit
|
### akka-multi-node-testkit
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-multi-node-testkit/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-multi-node-testkit/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-persistence-typed"></a>
|
||||||
|
### akka-persistence-typed
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-persistence-typed/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-akka-persistence"></a>
|
<a id="config-akka-persistence"></a>
|
||||||
### akka-persistence
|
### akka-persistence
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-persistence/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-persistence/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-akka-remote"></a>
|
<a id="config-akka-persistence-query"></a>
|
||||||
### akka-remote
|
### akka-persistence-query
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-remote/src/main/resources/reference.conf) { #shared #classic type=none }
|
@@snip [reference.conf](/akka-persistence-query/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-akka-remote-artery"></a>
|
<a id="config-akka-remote-artery"></a>
|
||||||
### akka-remote (artery)
|
### akka-remote artery
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-remote/src/main/resources/reference.conf) { #shared #artery type=none }
|
@@snip [reference.conf](/akka-remote/src/main/resources/reference.conf) { #shared #artery type=none }
|
||||||
|
|
||||||
|
<a id="config-akka-remote"></a>
|
||||||
|
### akka-remote classic (deprecated)
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-remote/src/main/resources/reference.conf) { #shared #classic type=none }
|
||||||
|
|
||||||
<a id="config-akka-testkit"></a>
|
<a id="config-akka-testkit"></a>
|
||||||
### akka-testkit
|
### akka-testkit
|
||||||
|
|
||||||
|
|
@ -424,6 +424,11 @@ Each Akka module has a reference configuration file with the default values.
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-cluster-tools/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-cluster-tools/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-cluster-sharding-typed"></a>
|
||||||
|
### akka-cluster-sharding-typed
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-cluster-sharding-typed/src/main/resources/reference.conf)
|
||||||
|
|
||||||
<a id="config-cluster-sharding"></a>
|
<a id="config-cluster-sharding"></a>
|
||||||
### akka-cluster-sharding
|
### akka-cluster-sharding
|
||||||
|
|
||||||
|
|
@ -433,3 +438,14 @@ Each Akka module has a reference configuration file with the default values.
|
||||||
### akka-distributed-data
|
### akka-distributed-data
|
||||||
|
|
||||||
@@snip [reference.conf](/akka-distributed-data/src/main/resources/reference.conf)
|
@@snip [reference.conf](/akka-distributed-data/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-stream"></a>
|
||||||
|
### akka-stream
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-stream/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
<a id="config-akka-stream-testkit"></a>
|
||||||
|
### akka-stream-testkit
|
||||||
|
|
||||||
|
@@snip [reference.conf](/akka-stream-testkit/src/main/resources/reference.conf)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,13 @@ Such are the perils of synchronized.
|
||||||
|
|
||||||
Since Akka runs on the JVM there are still some rules to be followed.
|
Since Akka runs on the JVM there are still some rules to be followed.
|
||||||
|
|
||||||
* Closing over internal Actor state and exposing it to other threads
|
Most importantly, you must not close over internal Actor state and exposing it to other threads:
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [SharedMutableStateDocSpec.scala](/akka-docs/src/test/scala/docs/actor/typed/SharedMutableStateDocSpec.scala) { #mutable-state }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [DistributedDataDocTest.java](/akka-docs/src/test/java/jdocs/actor/typed/SharedMutableStateDocTest.java) { #mutable-state }
|
||||||
|
|
||||||
@@snip [SharedMutableStateDocSpec.scala](/akka-docs/src/test/scala/docs/actor/SharedMutableStateDocSpec.scala) { #mutable-state }
|
|
||||||
|
|
||||||
* Messages **should** be immutable, this is to avoid the shared mutable state trap.
|
* Messages **should** be immutable, this is to avoid the shared mutable state trap.
|
||||||
|
|
|
||||||
|
|
@ -275,9 +275,12 @@ acknowledgement
|
||||||
* a way for the receiver to detect and discard duplicates
|
* a way for the receiver to detect and discard duplicates
|
||||||
|
|
||||||
The third becomes necessary by virtue of the acknowledgements not being guaranteed
|
The third becomes necessary by virtue of the acknowledgements not being guaranteed
|
||||||
to arrive either. An ACK-RETRY protocol with business-level acknowledgements is
|
to arrive either.
|
||||||
supported by @ref:[At-Least-Once Delivery](../persistence.md#at-least-once-delivery) of the Akka Persistence module. Duplicates can be
|
|
||||||
detected by tracking the identifiers of messages sent via @ref:[At-Least-Once Delivery](../persistence.md#at-least-once-delivery).
|
An ACK-RETRY protocol with business-level acknowledgements and de-duplication using identifiers is
|
||||||
|
supported by the @ref:[At-Least-Once Delivery](../persistence.md#at-least-once-delivery) of the Classic Akka Persistence module.
|
||||||
|
Corresponding functionality for typed has not yet been implemented (see [issue #20984](https://github.com/akka/akka/issues/20984)).
|
||||||
|
|
||||||
Another way of implementing the third part would be to make processing the messages
|
Another way of implementing the third part would be to make processing the messages
|
||||||
idempotent on the level of the business logic.
|
idempotent on the level of the business logic.
|
||||||
|
|
||||||
|
|
@ -350,8 +353,8 @@ local system (if no network connection can be established) or the remote one
|
||||||
Every time an actor does not terminate by its own decision, there is a chance
|
Every time an actor does not terminate by its own decision, there is a chance
|
||||||
that some messages which it sends to itself are lost. There is one which
|
that some messages which it sends to itself are lost. There is one which
|
||||||
happens quite easily in complex shutdown scenarios that is usually benign:
|
happens quite easily in complex shutdown scenarios that is usually benign:
|
||||||
seeing a `akka.dispatch.Terminate` message dropped means that two
|
seeing instances of a graceful stop command for an actor being dropped means that two
|
||||||
termination requests were given, but only one can succeed. In the
|
stop requests were given, but only one can succeed. In the
|
||||||
same vein, you might see `akka.actor.Terminated` messages from children
|
same vein, you might see `akka.actor.Terminated` messages from children
|
||||||
while stopping a hierarchy of actors turning up in dead letters if the parent
|
while stopping a hierarchy of actors turning up in dead letters if the parent
|
||||||
is still watching the child when the parent terminates.
|
is still watching the child when the parent terminates.
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,7 @@ for a detailed discussion on why the second approach is bound to fail.
|
||||||
What is true of Akka need not be true of the application which uses it, since
|
What is true of Akka need not be true of the application which uses it, since
|
||||||
designing for distributed execution poses some restrictions on what is
|
designing for distributed execution poses some restrictions on what is
|
||||||
possible. The most obvious one is that all messages sent over the wire must be
|
possible. The most obvious one is that all messages sent over the wire must be
|
||||||
serializable. While being a little less obvious this includes closures which
|
serializable.
|
||||||
are used as actor factories (i.e. within `Props`) if the actor is to be
|
|
||||||
created on a remote node.
|
|
||||||
|
|
||||||
Another consequence is that everything needs to be aware of all interactions
|
Another consequence is that everything needs to be aware of all interactions
|
||||||
being fully asynchronous, which in a computer network might mean that it may
|
being fully asynchronous, which in a computer network might mean that it may
|
||||||
|
|
@ -32,19 +30,6 @@ configuration). It also means that the probability for a message to be lost is
|
||||||
much higher than within one JVM, where it is close to zero (still: no hard
|
much higher than within one JVM, where it is close to zero (still: no hard
|
||||||
guarantee!).
|
guarantee!).
|
||||||
|
|
||||||
## How is Remoting Used?
|
|
||||||
|
|
||||||
We took the idea of transparency to the limit in that there is nearly no API
|
|
||||||
for the remoting layer of Akka: it is purely driven by configuration. Just
|
|
||||||
write your application according to the principles outlined in the previous
|
|
||||||
sections, then specify remote deployment of actor sub-trees in the
|
|
||||||
configuration file. This way, your application can be scaled out without having
|
|
||||||
to touch the code. The only piece of the API which allows programmatic
|
|
||||||
influence on remote deployment is that `Props` contain a field which may
|
|
||||||
be set to a specific `Deploy` instance; this has the same effect as
|
|
||||||
putting an equivalent deployment into the configuration file (if both are
|
|
||||||
given, configuration file wins).
|
|
||||||
|
|
||||||
<a id="symmetric-communication"></a>
|
<a id="symmetric-communication"></a>
|
||||||
## Peer-to-Peer vs. Client-Server
|
## Peer-to-Peer vs. Client-Server
|
||||||
|
|
||||||
|
|
@ -74,11 +59,4 @@ In addition to being able to run different parts of an actor system on
|
||||||
different nodes of a cluster, it is also possible to scale up onto more cores
|
different nodes of a cluster, it is also possible to scale up onto more cores
|
||||||
by multiplying actor sub-trees which support parallelization (think for example
|
by multiplying actor sub-trees which support parallelization (think for example
|
||||||
a search engine processing different queries in parallel). The clones can then
|
a search engine processing different queries in parallel). The clones can then
|
||||||
be routed to in different fashions, e.g. round-robin. The only thing necessary
|
be routed to in different fashions, e.g. round-robin. See @ref:[Routing](../typed/routers.md) for more details.
|
||||||
to achieve this is that the developer needs to declare a certain actor as
|
|
||||||
“withRouter”, then—in its stead—a router actor will be created which will spawn
|
|
||||||
up a configurable number of children of the desired type and route to them in
|
|
||||||
the configured fashion. Once such a router has been declared, its configuration
|
|
||||||
can be freely overridden from the configuration file, including mixing it with
|
|
||||||
the remote deployment of (some of) the children. Read more about
|
|
||||||
this in @ref:[Routing](../routing.md).
|
|
||||||
|
|
|
||||||
|
|
@ -2,121 +2,55 @@
|
||||||
|
|
||||||
This chapter outlines the concept behind supervision, the primitives offered
|
This chapter outlines the concept behind supervision, the primitives offered
|
||||||
and their semantics. For details on how that translates into real code, please
|
and their semantics. For details on how that translates into real code, please
|
||||||
refer to the corresponding chapters for Scala and Java APIs.
|
refer to @ref:[supervision](../typed/fault-tolerance.md).
|
||||||
|
|
||||||
## Sample project
|
Supervision has changed since classic, for details on classic supervision see @ref:[Classic Supervision](../supervision-classic.md)
|
||||||
|
|
||||||
You can look at the
|
|
||||||
@extref[Supervision example project](samples:akka-samples-supervision-java)
|
|
||||||
to see what this looks like in practice.
|
|
||||||
|
|
||||||
<a id="supervision-directives"></a>
|
<a id="supervision-directives"></a>
|
||||||
## What Supervision Means
|
## What Supervision Means
|
||||||
|
|
||||||
As described in @ref:[Actor Systems](actor-systems.md) supervision describes a dependency
|
There are two categories of exception that can happen in an actor:
|
||||||
relationship between actors: the supervisor delegates tasks to subordinates and
|
|
||||||
therefore must respond to their failures. When a subordinate detects a failure
|
|
||||||
(i.e. throws an exception), it suspends itself and all its subordinates and
|
|
||||||
sends a message to its supervisor, signaling failure. Depending on the nature
|
|
||||||
of the work to be supervised and the nature of the failure, the supervisor has
|
|
||||||
a choice of the following four options:
|
|
||||||
|
|
||||||
1. Resume the subordinate, keeping its accumulated internal state
|
1. Input validation errors, expected exceptions which can be handled with a regular try-catch
|
||||||
2. Restart the subordinate, clearing out its accumulated internal state
|
or other language and standard library tools.
|
||||||
3. Stop the subordinate permanently
|
1. Unexpected **failures**, for example a network resource being unavailable, a disk write failing or perhaps
|
||||||
4. Escalate the failure, thereby failing itself
|
a bug in the application logic.
|
||||||
|
|
||||||
It is important to always view an actor as part of a supervision hierarchy,
|
Supervision deals with failures and should be separated from the business logic while validating data and handling
|
||||||
which explains the existence of the fourth choice (as a supervisor also is
|
of expected exceptions is a vital part of the business logic. Therefore supervision is added to an actor as decoration
|
||||||
subordinate to another supervisor higher up) and has implications on the first
|
rather than something that is intermingled with the message processing logic of the actor.
|
||||||
three: resuming an actor resumes all its subordinates, restarting an actor
|
|
||||||
entails restarting all its subordinates (but see below for more details),
|
|
||||||
similarly terminating an actor will also terminate all its subordinates. It
|
|
||||||
should be noted that the default behavior of the `preRestart` hook of the
|
|
||||||
`Actor` class is to terminate all its children before restarting, but
|
|
||||||
this hook can be overridden; the recursive restart applies to all children left
|
|
||||||
after this hook has been executed.
|
|
||||||
|
|
||||||
Each supervisor is configured with a function translating all possible failure
|
Depending on the nature of the work to be supervised and the nature of the failure, supervision
|
||||||
causes (i.e. exceptions) into one of the four choices given above; notably,
|
provides the following three strategies:
|
||||||
this function does not take the failed actor’s identity as an input. It is
|
|
||||||
quite easy to come up with examples of structures where this might not seem
|
|
||||||
flexible enough, e.g. wishing for different strategies to be applied to
|
|
||||||
different subordinates. At this point it is vital to understand that
|
|
||||||
supervision is about forming a recursive fault handling structure. If you try
|
|
||||||
to do too much at one level, it will become hard to reason about, hence the
|
|
||||||
recommended way in this case is to add a level of supervision.
|
|
||||||
|
|
||||||
Akka implements a specific form called “parental supervision”. Actors can only
|
1. Resume the actor, keeping its accumulated internal state
|
||||||
be created by other actors—where the top-level actor is provided by the
|
2. Restart the actor, clearing out its accumulated internal state, with a potential delay starting again
|
||||||
library—and each created actor is supervised by its parent. This restriction
|
3. Stop the actor permanently
|
||||||
makes the formation of actor supervision hierarchies implicit and encourages
|
|
||||||
sound design decisions. It should be noted that this also guarantees that
|
|
||||||
actors cannot be orphaned or attached to supervisors from the outside, which
|
|
||||||
might otherwise catch them unawares. In addition, this yields a natural and
|
|
||||||
clean shutdown procedure for (sub-trees of) actor applications.
|
|
||||||
|
|
||||||
@@@ warning
|
Since actors are part of a hierarchy it can often make sense to propagate
|
||||||
|
the permanent failures upwards, if all children of an actor has stopped
|
||||||
|
unexpectedly it may make sense for the actor itself to restart or stop to
|
||||||
|
get back to a functional state. This can achieved through a combination of
|
||||||
|
supervision and watching the children to get notified when they terminate.
|
||||||
|
An example of this can be found in @ref:[Bubble failures up through the hierarchy](../typed/fault-tolerance.md#bubble).
|
||||||
|
|
||||||
Supervision related parent-child communication happens by special system
|
## The Top-Level actors
|
||||||
messages that have their own mailboxes separate from user messages. This
|
|
||||||
implies that supervision related events are not deterministically
|
|
||||||
ordered relative to ordinary messages. In general, the user cannot influence
|
|
||||||
the order of normal messages and failure notifications. For details and
|
|
||||||
example see the @ref:[Discussion: Message Ordering](message-delivery-reliability.md#message-ordering) section.
|
|
||||||
|
|
||||||
@@@
|
An actor system will during its creation start at least two actors.
|
||||||
|
|
||||||
<a id="toplevel-supervisors"></a>
|
### `/user`: the user guardian cator
|
||||||
## The Top-Level Supervisors
|
|
||||||
|
|
||||||

|
This is the top level user provided actor, meant to bootstrap the application
|
||||||
|
by spawning subsystems as children. When the user guardian stops the entire
|
||||||
|
actor system is shut down.
|
||||||
|
|
||||||
An actor system will during its creation start at least three actors, shown in
|
### `/system`: the system guardian
|
||||||
the image above. For more information about the consequences for actor paths
|
|
||||||
see @ref:[Top-Level Scopes for Actor Paths](addressing.md#toplevel-paths).
|
|
||||||
|
|
||||||
<a id="user-guardian"></a>
|
|
||||||
### `/user`: The Guardian Actor
|
|
||||||
|
|
||||||
The actor which is probably most interacted with is the parent of all
|
|
||||||
user-created actors, the guardian named `"/user"`. Actors created using
|
|
||||||
`system.actorOf()` are children of this actor. This means that when this
|
|
||||||
guardian terminates, all normal actors in the system will be shutdown, too. It
|
|
||||||
also means that this guardian’s supervisor strategy determines how the
|
|
||||||
top-level normal actors are supervised. Since Akka 2.1 it is possible to
|
|
||||||
configure this using the setting `akka.actor.guardian-supervisor-strategy`,
|
|
||||||
which takes the fully-qualified class-name of a
|
|
||||||
`SupervisorStrategyConfigurator`. When the guardian escalates a failure,
|
|
||||||
the root guardian’s response will be to terminate the guardian, which in effect
|
|
||||||
will shut down the whole actor system.
|
|
||||||
|
|
||||||
### `/system`: The System Guardian
|
|
||||||
|
|
||||||
This special guardian has been introduced in order to achieve an orderly
|
This special guardian has been introduced in order to achieve an orderly
|
||||||
shut-down sequence where logging remains active while all normal actors
|
shut-down sequence where logging remains active while all normal actors
|
||||||
terminate, even though logging itself is implemented using actors. This is
|
terminate, even though logging itself is implemented using actors. This is
|
||||||
realized by having the system guardian watch the user guardian and initiate its own
|
realized by having the system guardian watch the user guardian and initiate its own
|
||||||
shut-down upon reception of the `Terminated` message. The top-level
|
shut-down upon having seen the user guardian stop.
|
||||||
system actors are supervised using a strategy which will restart indefinitely
|
|
||||||
upon all types of `Exception` except for
|
|
||||||
`ActorInitializationException` and `ActorKilledException`, which
|
|
||||||
will terminate the child in question. All other throwables are escalated,
|
|
||||||
which will shut down the whole actor system.
|
|
||||||
|
|
||||||
### `/`: The Root Guardian
|
|
||||||
|
|
||||||
The root guardian is the grand-parent of all so-called “top-level” actors and
|
|
||||||
supervises all the special actors mentioned in @ref:[Top-Level Scopes for Actor Paths](addressing.md#toplevel-paths) using the
|
|
||||||
`SupervisorStrategy.stoppingStrategy`, whose purpose is to terminate the
|
|
||||||
child upon any type of `Exception`. All other throwables will be
|
|
||||||
escalated … but to whom? Since every real actor has a supervisor, the
|
|
||||||
supervisor of the root guardian cannot be a real actor. And because this means
|
|
||||||
that it is “outside of the bubble”, it is called the “bubble-walker”. This is a
|
|
||||||
synthetic `ActorRef` which in effect stops its child upon the first sign
|
|
||||||
of trouble and sets the actor system’s `isTerminated` status to `true` as
|
|
||||||
soon as the root guardian is fully terminated (all children recursively
|
|
||||||
stopped).
|
|
||||||
|
|
||||||
<a id="supervision-restart"></a>
|
<a id="supervision-restart"></a>
|
||||||
## What Restarting Means
|
## What Restarting Means
|
||||||
|
|
@ -132,8 +66,8 @@ Unless the failure is specifically recognizable, the third cause cannot be
|
||||||
ruled out, which leads to the conclusion that the internal state needs to be
|
ruled out, which leads to the conclusion that the internal state needs to be
|
||||||
cleared out. If the supervisor decides that its other children or itself is not
|
cleared out. If the supervisor decides that its other children or itself is not
|
||||||
affected by the corruption—e.g. because of conscious application of the error
|
affected by the corruption—e.g. because of conscious application of the error
|
||||||
kernel pattern—it is therefore best to restart the child. This is carried out
|
kernel pattern—it is therefore best to restart the actor. This is carried out
|
||||||
by creating a new instance of the underlying `Actor` class and replacing
|
by creating a new instance of the underlying `Behavior` class and replacing
|
||||||
the failed instance with the fresh one inside the child’s `ActorRef`;
|
the failed instance with the fresh one inside the child’s `ActorRef`;
|
||||||
the ability to do this is one of the reasons for encapsulating actors within
|
the ability to do this is one of the reasons for encapsulating actors within
|
||||||
special references. The new actor then resumes processing its mailbox, meaning
|
special references. The new actor then resumes processing its mailbox, meaning
|
||||||
|
|
@ -141,22 +75,6 @@ that the restart is not visible outside of the actor itself with the notable
|
||||||
exception that the message during which the failure occurred is not
|
exception that the message during which the failure occurred is not
|
||||||
re-processed.
|
re-processed.
|
||||||
|
|
||||||
The precise sequence of events during a restart is the following:
|
|
||||||
|
|
||||||
1. suspend the actor (which means that it will not process normal messages until
|
|
||||||
resumed), and recursively suspend all children
|
|
||||||
2. call the old instance’s `preRestart` hook (defaults to sending
|
|
||||||
termination requests to all children and calling `postStop`)
|
|
||||||
3. wait for all children which were requested to terminate (using
|
|
||||||
`context.stop()`) during `preRestart` to actually terminate;
|
|
||||||
this—like all actor operations—is non-blocking, the termination notice from
|
|
||||||
the last killed child will effect the progression to the next step
|
|
||||||
4. create new actor instance by invoking the originally provided factory again
|
|
||||||
5. invoke `postRestart` on the new instance (which by default also calls `preStart`)
|
|
||||||
6. send restart request to all children which were not killed in step 3;
|
|
||||||
restarted children will follow the same process recursively, from step 2
|
|
||||||
7. resume the actor
|
|
||||||
|
|
||||||
## What Lifecycle Monitoring Means
|
## What Lifecycle Monitoring Means
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
@ -183,145 +101,6 @@ message will be delivered irrespective of the order in which the monitoring
|
||||||
request and target’s termination occur, i.e. you still get the message even if
|
request and target’s termination occur, i.e. you still get the message even if
|
||||||
at the time of registration the target is already dead.
|
at the time of registration the target is already dead.
|
||||||
|
|
||||||
Monitoring is particularly useful if a supervisor cannot restart its
|
|
||||||
children and has to terminate them, e.g. in case of errors during actor
|
|
||||||
initialization. In that case it should monitor those children and re-create
|
|
||||||
them or schedule itself to retry this at a later time.
|
|
||||||
|
|
||||||
Another common use case is that an actor needs to fail in the absence of an
|
|
||||||
external resource, which may also be one of its own children. If a third party
|
|
||||||
terminates a child by way of the `system.stop(child)` method or sending a
|
|
||||||
`PoisonPill`, the supervisor might well be affected.
|
|
||||||
|
|
||||||
<a id="backoff-supervisor"></a>
|
|
||||||
### Delayed restarts with the BackoffSupervisor pattern
|
|
||||||
|
|
||||||
Provided as a built-in pattern the `akka.pattern.BackoffSupervisor` implements the so-called
|
|
||||||
*exponential backoff supervision strategy*, starting a child actor again when it fails, each time with a growing time delay between restarts.
|
|
||||||
|
|
||||||
This pattern is useful when the started actor fails <a id="^1" href="#1">[1]</a> because some external resource is not available,
|
|
||||||
and we need to give it some time to start-up again. One of the prime examples when this is useful is
|
|
||||||
when a @ref:[PersistentActor](../persistence.md) fails (by stopping) with a persistence failure - which indicates that
|
|
||||||
the database may be down or overloaded, in such situations it makes most sense to give it a little bit of time
|
|
||||||
to recover before the persistent actor is started.
|
|
||||||
|
|
||||||
> <a id="1" href="#^1">[1]</a> A failure can be indicated in two different ways; by an actor stopping or crashing.
|
|
||||||
|
|
||||||
#### Supervision strategies
|
|
||||||
|
|
||||||
There are two basic supervision strategies available for backoff:
|
|
||||||
|
|
||||||
* 'On failure': The supervisor will terminate and then start the supervised actor if it crashes. If the supervised actor stops normally (e.g. through `context.stop`), the supervisor will be terminated and no further attempt to start the supervised actor will be done.
|
|
||||||
* 'On stop': The supervisor will terminate and then start the supervised actor if it terminates in any way (consider this for `PersistentActor` since they stop on persistence failures instead of crashing)
|
|
||||||
|
|
||||||
To note that this supervision strategy does not restart the actor but rather stops and starts it. Be aware of it if you
|
|
||||||
use @scala[`Stash` trait’s] @java[`AbstractActorWithStash`] in combination with the backoff supervision strategy.
|
|
||||||
The `preRestart` hook will not be executed if the supervised actor fails or stops and you will miss the opportunity
|
|
||||||
to unstash the messages.
|
|
||||||
|
|
||||||
#### Sharding
|
|
||||||
If the 'on stop' strategy is used for sharded actors a final termination message should be configured and used to terminate the actor on passivation. Otherwise the supervisor will just stop and start the actor again.
|
|
||||||
|
|
||||||
The termination message is configured with:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded }
|
|
||||||
|
|
||||||
And must be used for passivation:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded-passivation }
|
|
||||||
|
|
||||||
#### Simple backoff
|
|
||||||
|
|
||||||
The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has stopped
|
|
||||||
because of a failure, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-stop }
|
|
||||||
|
|
||||||
The above is equivalent to this Java code:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-imports }
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-stop }
|
|
||||||
|
|
||||||
Using a `randomFactor` to add a little bit of additional variance to the backoff intervals
|
|
||||||
is highly recommended, in order to avoid multiple actors re-start at the exact same point in time,
|
|
||||||
for example because they were stopped due to a shared resource such as a database going down
|
|
||||||
and re-starting after the same configured interval. By adding additional randomness to the
|
|
||||||
re-start intervals the actors will start in slightly different points in time, thus avoiding
|
|
||||||
large spikes of traffic hitting the recovering shared database or other resource that they all need to contact.
|
|
||||||
|
|
||||||
The `akka.pattern.BackoffSupervisor` actor can also be configured to stop and start the actor after a delay when the actor
|
|
||||||
crashes and the supervision strategy decides that it should restart.
|
|
||||||
|
|
||||||
The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has crashed
|
|
||||||
because of some exception, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-fail }
|
|
||||||
|
|
||||||
The above is equivalent to this Java code:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-imports }
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-fail }
|
|
||||||
|
|
||||||
#### Customization
|
|
||||||
|
|
||||||
The `akka.pattern.BackoffOnFailureOptions` and `akka.pattern.BackoffOnRestartOptions` can be used to customize the behavior of the back-off supervisor actor.
|
|
||||||
Options are:
|
|
||||||
* `withAutoReset`: The backoff is reset if no failure/stop occurs within the duration. This is the default behaviour with `minBackoff` as default value
|
|
||||||
* `withManualReset`: The child must send `BackoffSupervisor.Reset` to its backoff supervisor (parent)
|
|
||||||
* `withSupervisionStrategy`: Sets a custom `OneForOneStrategy` (as each backoff supervisor only has one child). The default strategy uses the `akka.actor.SupervisorStrategy.defaultDecider` which stops and starts the child on exceptions.
|
|
||||||
* `withMaxNrOfRetries`: Sets the maximum number of retries until the supervisor will give up (`-1` is default which means no limit of retries). Note: This is set on the supervision strategy, so setting a different strategy resets the `maxNrOfRetries`.
|
|
||||||
* `withReplyWhileStopped`: By default all messages received while the child is stopped are forwarded to dead letters. With this set, the supervisor will reply to the sender instead.
|
|
||||||
|
|
||||||
Only available on `BackoffOnStopOptions`:
|
|
||||||
* `withDefaultStoppingStrategy`: Sets a `OneForOneStrategy` with the stopping decider that stops the child on all exceptions.
|
|
||||||
* `withFinalStopMessage`: Allows to define a predicate to decide on finally stopping the child (and supervisor). Used for passivate sharded actors - see above.
|
|
||||||
|
|
||||||
Some examples:
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-stop }
|
|
||||||
|
|
||||||
The above code sets up a back-off supervisor that requires the child actor to send a `akka.pattern.BackoffSupervisor.Reset` message
|
|
||||||
to its parent when a message is successfully processed, resetting the back-off. It also uses a default stopping strategy, any exception
|
|
||||||
will cause the child to stop.
|
|
||||||
|
|
||||||
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-fail }
|
|
||||||
|
|
||||||
The above code sets up a back-off supervisor that stops and starts the child after back-off if MyException is thrown, any other exception will be
|
|
||||||
escalated. The back-off is automatically reset if the child does not throw any errors within 10 seconds.
|
|
||||||
|
|
||||||
## One-For-One Strategy vs. All-For-One Strategy
|
|
||||||
|
|
||||||
There are two classes of supervision strategies which come with Akka:
|
|
||||||
`OneForOneStrategy` and `AllForOneStrategy`. Both are configured
|
|
||||||
with a mapping from exception type to supervision directive (see
|
|
||||||
[above](#supervision-directives)) and limits on how often a child is allowed to fail
|
|
||||||
before terminating it. The difference between them is that the former applies
|
|
||||||
the obtained directive only to the failed child, whereas the latter applies it
|
|
||||||
to all siblings as well. Normally, you should use the
|
|
||||||
`OneForOneStrategy`, which also is the default if none is specified
|
|
||||||
explicitly.
|
|
||||||
|
|
||||||
The `AllForOneStrategy` is applicable in cases where the ensemble of
|
|
||||||
children has such tight dependencies among them, that a failure of one child
|
|
||||||
affects the function of the others, i.e. they are inextricably linked. Since a
|
|
||||||
restart does not clear out the mailbox, it often is best to terminate the children
|
|
||||||
upon failure and re-create them explicitly from the supervisor (by watching the
|
|
||||||
children’s lifecycle); otherwise you have to make sure that it is no problem
|
|
||||||
for any of the actors to receive a message which was queued before the restart
|
|
||||||
but processed afterwards.
|
|
||||||
|
|
||||||
Normally stopping a child (i.e. not in response to a failure) will not
|
|
||||||
automatically terminate the other children in an all-for-one strategy; this can
|
|
||||||
be done by watching their lifecycle: if the `Terminated` message
|
|
||||||
is not handled by the supervisor, it will throw a `DeathPactException`
|
|
||||||
which (depending on its supervisor) will restart it, and the default
|
|
||||||
`preRestart` action will terminate all children. Of course this can be
|
|
||||||
handled explicitly as well.
|
|
||||||
|
|
||||||
Please note that creating one-off actors from an all-for-one supervisor entails
|
|
||||||
that failures escalated by the temporary actor will affect all the permanent
|
|
||||||
ones. If this is not desired, install an intermediate supervisor; this can very
|
|
||||||
be done by declaring a router of size 1 for the worker, see
|
|
||||||
@ref:[Routing](../routing.md).
|
|
||||||
|
|
|
||||||
BIN
akka-docs/src/main/paradox/images/actor-paths-overview.png
Normal file
BIN
akka-docs/src/main/paradox/images/actor-paths-overview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
|
|
@ -17,6 +17,7 @@ To use Classic Akka Actors, you must add the following dependency in your projec
|
||||||
@@@ index
|
@@@ index
|
||||||
|
|
||||||
* [actors](actors.md)
|
* [actors](actors.md)
|
||||||
|
* [supervision overview](supervision-classic.md)
|
||||||
* [fault-tolerance](fault-tolerance.md)
|
* [fault-tolerance](fault-tolerance.md)
|
||||||
* [dispatchers](dispatchers.md)
|
* [dispatchers](dispatchers.md)
|
||||||
* [mailboxes](mailboxes.md)
|
* [mailboxes](mailboxes.md)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
* [index-actors](index-actors.md)
|
* [index-actors](index-actors.md)
|
||||||
* [index-cluster](index-cluster.md)
|
* [index-cluster](index-cluster.md)
|
||||||
* [index-network](index-network.md)
|
* [index-network](index-network.md)
|
||||||
* [index-utilities](classic/index-utilities.md)
|
* [index-utilities](index-utilities-classic.md)
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ To use Utilities, you must add the following dependency in your project:
|
||||||
|
|
||||||
@@@ index
|
@@@ index
|
||||||
|
|
||||||
* [event-bus](../event-bus.md)
|
* [event-bus](event-bus.md)
|
||||||
* [logging](../logging.md)
|
* [logging](logging.md)
|
||||||
* [scheduler](../scheduler.md)
|
* [scheduler](scheduler.md)
|
||||||
* [extending-akka](../extending-akka.md)
|
* [extending-akka](extending-akka.md)
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
@ -421,7 +421,7 @@ Scala
|
||||||
Java
|
Java
|
||||||
: @@snip [LambdaPersistenceDocTest.java](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java) { #backoff }
|
: @@snip [LambdaPersistenceDocTest.java](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java) { #backoff }
|
||||||
|
|
||||||
See @ref:[Supervision strategies](general/supervision.md#supervision-strategies) for more details about actor supervision.
|
See @ref:[Backoff Supervision strategies](fault-tolerance.md#supervision-strategies) for more details about actor supervision.
|
||||||
|
|
||||||
If persistence of an event is rejected before it is stored, e.g. due to serialization error,
|
If persistence of an event is rejected before it is stored, e.g. due to serialization error,
|
||||||
`onPersistRejected` will be invoked (logging a warning by default), and the actor continues with
|
`onPersistRejected` will be invoked (logging a warning by default), and the actor continues with
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ a resizer.
|
||||||
Sometimes, rather than having the router actor create its routees, it is desirable to create routees
|
Sometimes, rather than having the router actor create its routees, it is desirable to create routees
|
||||||
separately and provide them to the router for its use. You can do this by passing in
|
separately and provide them to the router for its use. You can do this by passing in
|
||||||
paths of the routees to the router's configuration. Messages will be sent with `ActorSelection`
|
paths of the routees to the router's configuration. Messages will be sent with `ActorSelection`
|
||||||
to these paths, wildcards can be and will result in the same @ref:[semantics as explicitly using `ActorSelection`](general/addressing.md#querying-the-logical-actor-hierarchy).
|
to these paths, wildcards can be and will result in the same semantics as explicitly using `ActorSelection`.
|
||||||
|
|
||||||
The example below shows how to create a router by providing it with the path strings of three
|
The example below shows how to create a router by providing it with the path strings of three
|
||||||
routee actors.
|
routee actors.
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,7 @@ Java
|
||||||
|
|
||||||
## Delayed restarts with a backoff operator
|
## Delayed restarts with a backoff operator
|
||||||
|
|
||||||
Just as Akka provides the @ref:[backoff supervision pattern for actors](../general/supervision.md#backoff-supervisor), Akka streams
|
Akka streams provides a `RestartSource`, `RestartSink` and `RestartFlow` for implementing the so-called *exponential backoff
|
||||||
also provides a `RestartSource`, `RestartSink` and `RestartFlow` for implementing the so-called *exponential backoff
|
|
||||||
supervision strategy*, starting an operator again when it fails or completes, each time with a growing time delay between restarts.
|
supervision strategy*, starting an operator again when it fails or completes, each time with a growing time delay between restarts.
|
||||||
|
|
||||||
This pattern is useful when the operator fails or completes because some external resource is not available
|
This pattern is useful when the operator fails or completes because some external resource is not available
|
||||||
|
|
|
||||||
157
akka-docs/src/main/paradox/supervision-classic.md
Normal file
157
akka-docs/src/main/paradox/supervision-classic.md
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
# Classic Supervision
|
||||||
|
|
||||||
|
This chapter outlines the concept behind the supervision in Akka Classic, for the
|
||||||
|
corresponding overview of the new APIs see @ref:[supervision](general/supervision.md)
|
||||||
|
|
||||||
|
## Sample project
|
||||||
|
|
||||||
|
You can look at the
|
||||||
|
@extref[Supervision example project](samples:akka-samples-supervision-java)
|
||||||
|
to see what this looks like in practice.
|
||||||
|
|
||||||
|
<a id="supervision-directives"></a>
|
||||||
|
## What Supervision Means
|
||||||
|
|
||||||
|
Supervision describes a dependency relationship between actors: the supervisor delegates tasks to subordinates and
|
||||||
|
therefore must respond to their failures. When a subordinate detects a failure
|
||||||
|
(i.e. throws an exception), it suspends itself and all its subordinates and
|
||||||
|
sends a message to its supervisor, signaling failure. Depending on the nature
|
||||||
|
of the work to be supervised and the nature of the failure, the supervisor has
|
||||||
|
a choice of the following four options:
|
||||||
|
|
||||||
|
1. Resume the subordinate, keeping its accumulated internal state
|
||||||
|
2. Restart the subordinate, clearing out its accumulated internal state
|
||||||
|
3. Stop the subordinate permanently
|
||||||
|
4. Escalate the failure, thereby failing itself
|
||||||
|
|
||||||
|
It is important to always view an actor as part of a supervision hierarchy,
|
||||||
|
which explains the existence of the fourth choice (as a supervisor also is
|
||||||
|
subordinate to another supervisor higher up) and has implications on the first
|
||||||
|
three: resuming an actor resumes all its subordinates, restarting an actor
|
||||||
|
entails restarting all its subordinates (but see below for more details),
|
||||||
|
similarly terminating an actor will also terminate all its subordinates. It
|
||||||
|
should be noted that the default behavior of the `preRestart` hook of the
|
||||||
|
`Actor` class is to terminate all its children before restarting, but
|
||||||
|
this hook can be overridden; the recursive restart applies to all children left
|
||||||
|
after this hook has been executed.
|
||||||
|
|
||||||
|
Each supervisor is configured with a function translating all possible failure
|
||||||
|
causes (i.e. exceptions) into one of the four choices given above; notably,
|
||||||
|
this function does not take the failed actor’s identity as an input. It is
|
||||||
|
quite easy to come up with examples of structures where this might not seem
|
||||||
|
flexible enough, e.g. wishing for different strategies to be applied to
|
||||||
|
different subordinates. At this point it is vital to understand that
|
||||||
|
supervision is about forming a recursive fault handling structure. If you try
|
||||||
|
to do too much at one level, it will become hard to reason about, hence the
|
||||||
|
recommended way in this case is to add a level of supervision.
|
||||||
|
|
||||||
|
Akka implements a specific form called “parental supervision”. Actors can only
|
||||||
|
be created by other actors—where the top-level actor is provided by the
|
||||||
|
library—and each created actor is supervised by its parent. This restriction
|
||||||
|
makes the formation of actor supervision hierarchies implicit and encourages
|
||||||
|
sound design decisions. It should be noted that this also guarantees that
|
||||||
|
actors cannot be orphaned or attached to supervisors from the outside, which
|
||||||
|
might otherwise catch them unawares. In addition, this yields a natural and
|
||||||
|
clean shutdown procedure for (sub-trees of) actor applications.
|
||||||
|
|
||||||
|
@@@ warning
|
||||||
|
|
||||||
|
Supervision related parent-child communication happens by special system
|
||||||
|
messages that have their own mailboxes separate from user messages. This
|
||||||
|
implies that supervision related events are not deterministically
|
||||||
|
ordered relative to ordinary messages. In general, the user cannot influence
|
||||||
|
the order of normal messages and failure notifications. For details and
|
||||||
|
example see the @ref:[Discussion: Message Ordering](general/message-delivery-reliability.md#message-ordering) section.
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
||||||
|
<a id="toplevel-supervisors"></a>
|
||||||
|
## The Top-Level Supervisors
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
An actor system will during its creation start at least three actors, shown in
|
||||||
|
the image above. For more information about the consequences for actor paths
|
||||||
|
see @ref:[Top-Level Scopes for Actor Paths](general/addressing.md#toplevel-paths).
|
||||||
|
|
||||||
|
<a id="user-guardian"></a>
|
||||||
|
### `/user`: The Guardian Actor
|
||||||
|
|
||||||
|
The actor which is probably most interacted with is the parent of all
|
||||||
|
user-created actors, the guardian named `"/user"`. Actors created using
|
||||||
|
`system.actorOf()` are children of this actor. This means that when this
|
||||||
|
guardian terminates, all normal actors in the system will be shutdown, too. It
|
||||||
|
also means that this guardian’s supervisor strategy determines how the
|
||||||
|
top-level normal actors are supervised. Since Akka 2.1 it is possible to
|
||||||
|
configure this using the setting `akka.actor.guardian-supervisor-strategy`,
|
||||||
|
which takes the fully-qualified class-name of a
|
||||||
|
`SupervisorStrategyConfigurator`. When the guardian escalates a failure,
|
||||||
|
the root guardian’s response will be to terminate the guardian, which in effect
|
||||||
|
will shut down the whole actor system.
|
||||||
|
|
||||||
|
### `/system`: The System Guardian
|
||||||
|
|
||||||
|
This special guardian has been introduced in order to achieve an orderly
|
||||||
|
shut-down sequence where logging remains active while all normal actors
|
||||||
|
terminate, even though logging itself is implemented using actors. This is
|
||||||
|
realized by having the system guardian watch the user guardian and initiate its own
|
||||||
|
shut-down upon reception of the `Terminated` message. The top-level
|
||||||
|
system actors are supervised using a strategy which will restart indefinitely
|
||||||
|
upon all types of `Exception` except for
|
||||||
|
`ActorInitializationException` and `ActorKilledException`, which
|
||||||
|
will terminate the child in question. All other throwables are escalated,
|
||||||
|
which will shut down the whole actor system.
|
||||||
|
|
||||||
|
### `/`: The Root Guardian
|
||||||
|
|
||||||
|
The root guardian is the grand-parent of all so-called “top-level” actors and
|
||||||
|
supervises all the special actors mentioned in @ref:[Top-Level Scopes for Actor Paths](general/addressing.md#toplevel-paths) using the
|
||||||
|
`SupervisorStrategy.stoppingStrategy`, whose purpose is to terminate the
|
||||||
|
child upon any type of `Exception`. All other throwables will be
|
||||||
|
escalated … but to whom? Since every real actor has a supervisor, the
|
||||||
|
supervisor of the root guardian cannot be a real actor. And because this means
|
||||||
|
that it is “outside of the bubble”, it is called the “bubble-walker”. This is a
|
||||||
|
synthetic `ActorRef` which in effect stops its child upon the first sign
|
||||||
|
of trouble and sets the actor system’s `isTerminated` status to `true` as
|
||||||
|
soon as the root guardian is fully terminated (all children recursively
|
||||||
|
stopped).
|
||||||
|
|
||||||
|
|
||||||
|
## One-For-One Strategy vs. All-For-One Strategy
|
||||||
|
|
||||||
|
There are two classes of supervision strategies which come with Akka:
|
||||||
|
`OneForOneStrategy` and `AllForOneStrategy`. Both are configured
|
||||||
|
with a mapping from exception type to supervision directive (see
|
||||||
|
[above](#supervision-directives)) and limits on how often a child is allowed to fail
|
||||||
|
before terminating it. The difference between them is that the former applies
|
||||||
|
the obtained directive only to the failed child, whereas the latter applies it
|
||||||
|
to all siblings as well. Normally, you should use the
|
||||||
|
`OneForOneStrategy`, which also is the default if none is specified
|
||||||
|
explicitly.
|
||||||
|
|
||||||
|
The `AllForOneStrategy` is applicable in cases where the ensemble of
|
||||||
|
children has such tight dependencies among them, that a failure of one child
|
||||||
|
affects the function of the others, i.e. they are inextricably linked. Since a
|
||||||
|
restart does not clear out the mailbox, it often is best to terminate the children
|
||||||
|
upon failure and re-create them explicitly from the supervisor (by watching the
|
||||||
|
children’s lifecycle); otherwise you have to make sure that it is no problem
|
||||||
|
for any of the actors to receive a message which was queued before the restart
|
||||||
|
but processed afterwards.
|
||||||
|
|
||||||
|
Normally stopping a child (i.e. not in response to a failure) will not
|
||||||
|
automatically terminate the other children in an all-for-one strategy; this can
|
||||||
|
be done by watching their lifecycle: if the `Terminated` message
|
||||||
|
is not handled by the supervisor, it will throw a `DeathPactException`
|
||||||
|
which (depending on its supervisor) will restart it, and the default
|
||||||
|
`preRestart` action will terminate all children. Of course this can be
|
||||||
|
handled explicitly as well.
|
||||||
|
|
||||||
|
Please note that creating one-off actors from an all-for-one supervisor entails
|
||||||
|
that failures escalated by the temporary actor will affect all the permanent
|
||||||
|
ones. If this is not desired, install an intermediate supervisor; this can very
|
||||||
|
be done by declaring a router of size 1 for the worker, see
|
||||||
|
@ref:[Routing](routing.md).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,7 +32,12 @@ with a fresh state that we know is valid.
|
||||||
|
|
||||||
## Supervision
|
## Supervision
|
||||||
|
|
||||||
In Akka this "somewhere else" is called supervision. Supervision allows you to declaratively describe what should happen when a certain type of exceptions are thrown inside an actor. To use supervision the actual Actor behavior is wrapped using `Behaviors.supervise`, for example to restart on `IllegalStateExceptions`:
|
In Akka this "somewhere else" is called supervision. Supervision allows you to declaratively describe what should happen when a certain type of exceptions are thrown inside an actor.
|
||||||
|
|
||||||
|
To use supervision the actual Actor behavior is wrapped using `Behaviors.supervise`. Typically you would wrap the actor
|
||||||
|
with supervision in the parent when spawning it as a child.
|
||||||
|
|
||||||
|
This example restarts the actor when it fails with an `IllegalStateException`:
|
||||||
|
|
||||||
|
|
||||||
Scala
|
Scala
|
||||||
|
|
@ -140,6 +145,7 @@ Scala
|
||||||
Java
|
Java
|
||||||
: @@snip [SupervisionCompileOnlyTest.java](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/supervision/SupervisionCompileOnlyTest.java) { #restart-PreRestart-signal }
|
: @@snip [SupervisionCompileOnlyTest.java](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/supervision/SupervisionCompileOnlyTest.java) { #restart-PreRestart-signal }
|
||||||
|
|
||||||
|
<a id="bubble"/>
|
||||||
## Bubble failures up through the hierarchy
|
## Bubble failures up through the hierarchy
|
||||||
|
|
||||||
In some scenarios it may be useful to push the decision about what to do on a failure upwards in the Actor hierarchy
|
In some scenarios it may be useful to push the decision about what to do on a failure upwards in the Actor hierarchy
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdocs.actor.typed;
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef;
|
||||||
|
import akka.actor.typed.Behavior;
|
||||||
|
import akka.actor.typed.javadsl.AbstractBehavior;
|
||||||
|
import akka.actor.typed.javadsl.ActorContext;
|
||||||
|
import akka.actor.typed.javadsl.Receive;
|
||||||
|
import akka.actor.typed.javadsl.AskPattern;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
|
||||||
|
interface SharedMutableStateDocTest {
|
||||||
|
|
||||||
|
static CompletableFuture<String> expensiveCalculation() {
|
||||||
|
throw new UnsupportedOperationException("just a sample signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
class Query {
|
||||||
|
public final ActorRef<String> replyTo;
|
||||||
|
|
||||||
|
public Query(ActorRef<String> replyTo) {
|
||||||
|
this.replyTo = replyTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #mutable-state
|
||||||
|
class MyActor extends AbstractBehavior<MyActor.Command> {
|
||||||
|
|
||||||
|
interface Command {}
|
||||||
|
|
||||||
|
class Message implements Command {
|
||||||
|
public final ActorRef<Object> otherActor;
|
||||||
|
|
||||||
|
public Message(ActorRef<Object> replyTo) {
|
||||||
|
this.otherActor = replyTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateState implements Command {
|
||||||
|
public final String newState;
|
||||||
|
|
||||||
|
public UpdateState(String newState) {
|
||||||
|
this.newState = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ActorContext<Command> context;
|
||||||
|
|
||||||
|
private String state = "";
|
||||||
|
private Set<String> mySet = new HashSet<>();
|
||||||
|
|
||||||
|
public MyActor(ActorContext<Command> context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Receive<Command> createReceive() {
|
||||||
|
return newReceiveBuilder()
|
||||||
|
.onMessage(Message.class, this::onMessage)
|
||||||
|
.onMessage(UpdateState.class, this::onUpdateState)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<Command> onMessage(Message message) {
|
||||||
|
// Very bad: shared mutable object allows
|
||||||
|
// the other actor to mutate your own state,
|
||||||
|
// or worse, you might get weird race conditions
|
||||||
|
message.otherActor.tell(mySet);
|
||||||
|
|
||||||
|
// Example of incorrect approach
|
||||||
|
// Very bad: shared mutable state will cause your
|
||||||
|
// application to break in weird ways
|
||||||
|
CompletableFuture.runAsync(
|
||||||
|
() -> {
|
||||||
|
state = "This will race";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Example of incorrect approach
|
||||||
|
// Very bad: shared mutable state will cause your
|
||||||
|
// application to break in weird ways
|
||||||
|
expensiveCalculation()
|
||||||
|
.whenComplete(
|
||||||
|
(result, failure) -> {
|
||||||
|
if (result != null) state = "new state: " + result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Example of correct approach
|
||||||
|
// Turn the future result into a message that is sent to
|
||||||
|
// self when future completes
|
||||||
|
CompletableFuture<String> futureResult = expensiveCalculation();
|
||||||
|
context.pipeToSelf(
|
||||||
|
futureResult,
|
||||||
|
(result, failure) -> {
|
||||||
|
if (result != null) return new UpdateState(result);
|
||||||
|
else throw new RuntimeException(failure);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Another example of incorrect approach
|
||||||
|
// mutating actor state from ask future callback
|
||||||
|
CompletionStage<String> response =
|
||||||
|
AskPattern.ask(
|
||||||
|
message.otherActor,
|
||||||
|
Query::new,
|
||||||
|
Duration.ofSeconds(3),
|
||||||
|
context.getSystem().scheduler());
|
||||||
|
response.whenComplete(
|
||||||
|
(result, failure) -> {
|
||||||
|
if (result != null) state = "new state: " + result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// use context.ask instead, turns the completion
|
||||||
|
// into a message sent to self
|
||||||
|
context.ask(
|
||||||
|
String.class,
|
||||||
|
message.otherActor,
|
||||||
|
Duration.ofSeconds(3),
|
||||||
|
Query::new,
|
||||||
|
(result, failure) -> {
|
||||||
|
if (result != null) return new UpdateState(result);
|
||||||
|
else throw new RuntimeException(failure);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Behavior<Command> onUpdateState(UpdateState command) {
|
||||||
|
// safe as long as `newState` is immutable, if it is mutable we'd need to
|
||||||
|
// make a defensive copy
|
||||||
|
this.state = command.newState;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #mutable-state
|
||||||
|
}
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package jdocs.config;
|
|
||||||
|
|
||||||
import akka.actor.ActorSystem;
|
|
||||||
import com.typesafe.config.*;
|
|
||||||
|
|
||||||
public class ConfigDoc {
|
|
||||||
public ActorSystem createConfiguredSystem() {
|
|
||||||
// #java-custom-config
|
|
||||||
// make a Config with just your special setting
|
|
||||||
Config myConfig = ConfigFactory.parseString("something=somethingElse");
|
|
||||||
// load the normal config stack (system props,
|
|
||||||
// then application.conf, then reference.conf)
|
|
||||||
Config regularConfig = ConfigFactory.load();
|
|
||||||
// override regular stack with myConfig
|
|
||||||
Config combined = myConfig.withFallback(regularConfig);
|
|
||||||
// put the result in between the overrides
|
|
||||||
// (system props) and defaults again
|
|
||||||
Config complete = ConfigFactory.load(combined);
|
|
||||||
// create ActorSystem
|
|
||||||
ActorSystem system = ActorSystem.create("myname", complete);
|
|
||||||
// #java-custom-config
|
|
||||||
return system;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
akka-docs/src/test/java/jdocs/config/ConfigDocTest.java
Normal file
70
akka-docs/src/test/java/jdocs/config/ConfigDocTest.java
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdocs.config;
|
||||||
|
|
||||||
|
// #imports
|
||||||
|
import akka.actor.typed.ActorSystem;
|
||||||
|
import akka.actor.typed.Behavior;
|
||||||
|
import akka.actor.typed.javadsl.Behaviors;
|
||||||
|
import com.typesafe.config.Config;
|
||||||
|
import com.typesafe.config.ConfigFactory;
|
||||||
|
|
||||||
|
// #imports
|
||||||
|
import akka.actor.testkit.typed.javadsl.ActorTestKit;
|
||||||
|
|
||||||
|
public class ConfigDocTest {
|
||||||
|
|
||||||
|
private Behavior<Void> rootBehavior = Behaviors.empty();
|
||||||
|
|
||||||
|
public void customConfig() {
|
||||||
|
// #custom-config
|
||||||
|
Config customConf = ConfigFactory.parseString("akka.log-config-on-start = on");
|
||||||
|
// ConfigFactory.load sandwiches customConfig between default reference
|
||||||
|
// config and default overrides, and then resolves it.
|
||||||
|
ActorSystem<Void> system =
|
||||||
|
ActorSystem.create(rootBehavior, "MySystem", ConfigFactory.load(customConf));
|
||||||
|
// #custom-config
|
||||||
|
|
||||||
|
ActorTestKit.shutdown(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compileOnlyPrintConfig() {
|
||||||
|
// #dump-config
|
||||||
|
ActorSystem<Void> system = ActorSystem.create(rootBehavior, "MySystem");
|
||||||
|
system.logConfiguration();
|
||||||
|
// #dump-config
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compileOnlySeparateApps() {
|
||||||
|
// #separate-apps
|
||||||
|
Config config = ConfigFactory.load();
|
||||||
|
ActorSystem<Void> app1 =
|
||||||
|
ActorSystem.create(rootBehavior, "MyApp1", config.getConfig("myapp1").withFallback(config));
|
||||||
|
ActorSystem<Void> app2 =
|
||||||
|
ActorSystem.create(
|
||||||
|
rootBehavior,
|
||||||
|
"MyApp2",
|
||||||
|
config.getConfig("myapp2").withOnlyPath("akka").withFallback(config));
|
||||||
|
// #separate-apps
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorSystem createConfiguredSystem() {
|
||||||
|
// #custom-config-2
|
||||||
|
// make a Config with just your special setting
|
||||||
|
Config myConfig = ConfigFactory.parseString("something=somethingElse");
|
||||||
|
// load the normal config stack (system props,
|
||||||
|
// then application.conf, then reference.conf)
|
||||||
|
Config regularConfig = ConfigFactory.load();
|
||||||
|
// override regular stack with myConfig
|
||||||
|
Config combined = myConfig.withFallback(regularConfig);
|
||||||
|
// put the result in between the overrides
|
||||||
|
// (system props) and defaults again
|
||||||
|
Config complete = ConfigFactory.load(combined);
|
||||||
|
// create ActorSystem
|
||||||
|
ActorSystem system = ActorSystem.create(rootBehavior, "myname", complete);
|
||||||
|
// #custom-config-2
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docs.actor.typed
|
||||||
|
|
||||||
|
import akka.actor.typed.scaladsl._
|
||||||
|
import akka.actor.typed.{ ActorRef, Behavior }
|
||||||
|
import akka.util.Timeout
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
|
|
||||||
|
class SharedMutableStateDocSpec {
|
||||||
|
|
||||||
|
def expensiveCalculation(): Future[String] = ???
|
||||||
|
|
||||||
|
object MyActor {
|
||||||
|
trait Command
|
||||||
|
case class Message(msg: String, replyTo: ActorRef[Any]) extends Command
|
||||||
|
case class UpdateState(newState: String) extends Command
|
||||||
|
|
||||||
|
case class Query(replyTo: ActorRef[String])
|
||||||
|
|
||||||
|
def apply(): Behavior[Command] = Behaviors.setup { context =>
|
||||||
|
new MyActor(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#mutable-state
|
||||||
|
class MyActor(context: ActorContext[MyActor.Command]) extends AbstractBehavior[MyActor.Command] {
|
||||||
|
import MyActor._
|
||||||
|
|
||||||
|
var state = ""
|
||||||
|
val mySet = mutable.Set[String]()
|
||||||
|
|
||||||
|
def onMessage(cmd: MyActor.Command) = cmd match {
|
||||||
|
case Message(text, otherActor) =>
|
||||||
|
// Very bad: shared mutable object allows
|
||||||
|
// the other actor to mutate your own state,
|
||||||
|
// or worse, you might get weird race conditions
|
||||||
|
otherActor ! mySet
|
||||||
|
|
||||||
|
implicit val ec = context.executionContext
|
||||||
|
|
||||||
|
// Example of incorrect approach
|
||||||
|
// Very bad: shared mutable state will cause your
|
||||||
|
// application to break in weird ways
|
||||||
|
Future { state = "This will race" }
|
||||||
|
|
||||||
|
// Example of incorrect approach
|
||||||
|
// Very bad: shared mutable state will cause your
|
||||||
|
// application to break in weird ways
|
||||||
|
expensiveCalculation().foreach { result =>
|
||||||
|
state = s"new state: $result"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example of correct approach
|
||||||
|
// Turn the future result into a message that is sent to
|
||||||
|
// self when future completes
|
||||||
|
val futureResult = expensiveCalculation()
|
||||||
|
context.pipeToSelf(futureResult) {
|
||||||
|
case Success(result) => UpdateState(result)
|
||||||
|
case Failure(ex) => throw ex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another example of incorrect approach
|
||||||
|
// mutating actor state from ask future callback
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
implicit val timeout = Timeout(5.seconds) // needed for `ask` below
|
||||||
|
implicit val scheduler = context.system.scheduler
|
||||||
|
val future: Future[String] = otherActor.ask(Query)
|
||||||
|
future.foreach { result =>
|
||||||
|
state = result
|
||||||
|
}
|
||||||
|
|
||||||
|
// use context.ask instead, turns the completion
|
||||||
|
// into a message sent to self
|
||||||
|
context.ask(otherActor, Query) {
|
||||||
|
case Success(result) => UpdateState(result)
|
||||||
|
case Failure(ex) => throw ex
|
||||||
|
}
|
||||||
|
this
|
||||||
|
|
||||||
|
case UpdateState(newState) =>
|
||||||
|
// safe as long as `newState` is immutable, if it is mutable we'd need to
|
||||||
|
// make a defensive copy
|
||||||
|
state = newState
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#mutable-state
|
||||||
|
}
|
||||||
|
|
@ -4,33 +4,63 @@
|
||||||
|
|
||||||
package docs.config
|
package docs.config
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
import org.scalatest.Matchers
|
import org.scalatest.Matchers
|
||||||
import akka.testkit.TestKit
|
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.typed.ActorSystem
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
//#imports
|
//#imports
|
||||||
|
|
||||||
class ConfigDocSpec extends WordSpec with Matchers {
|
class ConfigDocSpec extends WordSpec with Matchers {
|
||||||
|
val rootBehavior = Behaviors.empty[String]
|
||||||
|
|
||||||
"programmatically configure ActorSystem" in {
|
"programmatically configure ActorSystem" in {
|
||||||
|
|
||||||
//#custom-config
|
//#custom-config
|
||||||
val customConf = ConfigFactory.parseString("""
|
val customConf = ConfigFactory.parseString("""
|
||||||
akka.actor.deployment {
|
akka.log-config-on-start = on
|
||||||
/my-service {
|
""")
|
||||||
router = round-robin-pool
|
|
||||||
nr-of-instances = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
// ConfigFactory.load sandwiches customConfig between default reference
|
// ConfigFactory.load sandwiches customConfig between default reference
|
||||||
// config and default overrides, and then resolves it.
|
// config and default overrides, and then resolves it.
|
||||||
val system = ActorSystem("MySystem", ConfigFactory.load(customConf))
|
val system = ActorSystem(rootBehavior, "MySystem", ConfigFactory.load(customConf))
|
||||||
//#custom-config
|
//#custom-config
|
||||||
|
|
||||||
TestKit.shutdownActorSystem(system)
|
ActorTestKit.shutdown(system)
|
||||||
|
}
|
||||||
|
|
||||||
|
def compileOnlyPrintConfig(): Unit = {
|
||||||
|
// #dump-config
|
||||||
|
val system = ActorSystem(rootBehavior, "MySystem")
|
||||||
|
system.logConfiguration()
|
||||||
|
// #dump-config
|
||||||
|
}
|
||||||
|
|
||||||
|
def compileOnlySeparateApps(): Unit = {
|
||||||
|
// #separate-apps
|
||||||
|
val config = ConfigFactory.load()
|
||||||
|
val app1 = ActorSystem(rootBehavior, "MyApp1", config.getConfig("myapp1").withFallback(config))
|
||||||
|
val app2 = ActorSystem(rootBehavior, "MyApp2", config.getConfig("myapp2").withOnlyPath("akka").withFallback(config))
|
||||||
|
// #separate-apps
|
||||||
|
}
|
||||||
|
|
||||||
|
def moreCustomConfig(): Unit = {
|
||||||
|
// #custom-config-2
|
||||||
|
// make a Config with just your special setting
|
||||||
|
val myConfig = ConfigFactory.parseString("something=somethingElse");
|
||||||
|
// load the normal config stack (system props,
|
||||||
|
// then application.conf, then reference.conf)
|
||||||
|
val regularConfig = ConfigFactory.load();
|
||||||
|
// override regular stack with myConfig
|
||||||
|
val combined = myConfig.withFallback(regularConfig);
|
||||||
|
// put the result in between the overrides
|
||||||
|
// (system props) and defaults again
|
||||||
|
val complete = ConfigFactory.load(combined);
|
||||||
|
// create ActorSystem
|
||||||
|
val system = ActorSystem(rootBehavior, "myname", complete);
|
||||||
|
// #custom-config-2
|
||||||
}
|
}
|
||||||
|
|
||||||
"deployment section" in {
|
"deployment section" in {
|
||||||
|
|
@ -76,7 +106,7 @@ class ConfigDocSpec extends WordSpec with Matchers {
|
||||||
}
|
}
|
||||||
#//#deployment-section
|
#//#deployment-section
|
||||||
""")
|
""")
|
||||||
val system = ActorSystem("MySystem", conf)
|
val system = ActorSystem(rootBehavior, "MySystem", conf)
|
||||||
TestKit.shutdownActorSystem(system)
|
ActorTestKit.shutdown(system)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue