diff --git a/akka-docs/rst/general/actor-systems.rst b/akka-docs/rst/general/actor-systems.rst index 1b7d6a7759..99df8bd334 100644 --- a/akka-docs/rst/general/actor-systems.rst +++ b/akka-docs/rst/general/actor-systems.rst @@ -89,10 +89,9 @@ Actor Best Practices bothering everyone else needlessly and avoid hogging resources. Translated to programming this means to process events and generate responses (or more requests) in an event-driven manner. Actors should not block (i.e. passively - wait while occupying a Thread) on some external entity, which might be a - lock, a network socket, etc. The blocking operations should be done in some - special-cased thread which sends messages to the actors which shall act on - them. + wait while occupying a Thread) on some external entity—which might be a + lock, a network socket, etc.—unless it is unavoidable; in the latter case + see below. #. Do not pass mutable objects between actors. In order to ensure that, prefer immutable messages. If the encapsulation of actors is broken by exposing @@ -112,6 +111,53 @@ Actor Best Practices performance) and it also reduces the number of blocking calls made, since the creation of top-level actors involves synchronous messaging. +Blocking Needs Careful Management +--------------------------------- + +In some cases it is unavoidable to do blocking operations, i.e. to put a thread +to sleep for an indeterminate time, waiting for an external event to occur. +Examples are legacy RDBMS drivers or messaging APIs, and the underlying reason +in typically that (network) I/O occurs under the covers. When facing this, you +may be tempted to just wrap the blocking call inside a :class:`Future` and work +with that instead, but this strategy is too simple: you are quite likely to +find bottle-necks or run out of memory or threads when the application runs +under increased load. + +The non-exhaustive list of adequate solutions to the “blocking problem” +includes the following suggestions: + + - Do the blocking call within an actor (or a set of actors managed by a router + [:ref:`Java `, :ref:`Scala `]), making sure to + configure a thread pool which is either dedicated for this purpose or + sufficiently sized. + + - Do the blocking call within a :class:`Future`, ensuring an upper bound on + the number of such calls at any point in time (submitting an unbounded + number of tasks of this nature will exhaust your memory or thread limits). + + - Do the blocking call within a :class:`Future`, providing a thread pool with + an upper limit on the number of threads which is appropriate for the + hardware on which the application runs. + + - Dedicate a single thread to manage a set of blocking resources (e.g. a NIO + selector driving multiple channels) and dispatch events as they occur as + actor messages. + +The first possibility is especially well-suited for resources which are +single-threaded in nature, like database handles which traditionally can only +execute one outstanding query at a time and use internal synchronization to +ensure this. A common pattern is to create a router for N actors, each of which +wraps a single DB connection and handles queries as sent to the router. The +number N must then be tuned for maximum throughput, which will vary depending +on which DBMS is deployed on what hardware. + +.. note:: + + Configuring thread pools is a task best delegated to Akka, simply configure + in the ``application.conf`` and instantiate through an :class:`ActorSystem` + [:ref:`Java `, :ref:`Scala + `] + What you should not concern yourself with ----------------------------------------- diff --git a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTestBase.java b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTestBase.java index 04705d524c..5c0c2d1711 100644 --- a/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTestBase.java +++ b/akka-docs/rst/java/code/docs/dispatcher/DispatcherDocTestBase.java @@ -5,10 +5,6 @@ package docs.dispatcher; //#imports import akka.actor.*; -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.actor.UntypedActor; -import akka.actor.UntypedActorFactory; //#imports //#imports-prio @@ -37,6 +33,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import scala.Option; +import scala.concurrent.ExecutionContext; import com.typesafe.config.ConfigFactory; @@ -75,6 +72,14 @@ public class DispatcherDocTestBase { .withDispatcher("my-pinned-dispatcher")); //#defining-pinned-dispatcher } + + public void compileLookup() { + //#lookup + // this is scala.concurrent.ExecutionContext + // for use with Futures, Scheduler, etc. + final ExecutionContext ex = system.dispatchers().lookup("my-dispatcher"); + //#lookup + } @Test public void priorityDispatcher() throws Exception { diff --git a/akka-docs/rst/java/dispatchers.rst b/akka-docs/rst/java/dispatchers.rst index 7fa54e0529..b25a1c33b0 100644 --- a/akka-docs/rst/java/dispatchers.rst +++ b/akka-docs/rst/java/dispatchers.rst @@ -13,6 +13,15 @@ Default dispatcher Every ``ActorSystem`` will have a default dispatcher that will be used in case nothing else is configured for an ``Actor``. The default dispatcher can be configured, and is by default a ``Dispatcher`` with a "fork-join-executor", which gives excellent performance in most cases. +.. _dispatcher-lookup-java: + +Looking up a Dispatcher +----------------------- + +Dispatchers implement the :class:`ExecutionContext` interface and can thus be used to run :class:`Future` invocations etc. + +.. includecode:: code/docs/dispatcher/DispatcherDocTestBase.java#lookup + Setting the dispatcher for an Actor ----------------------------------- diff --git a/akka-docs/rst/scala/code/docs/dispatcher/DispatcherDocSpec.scala b/akka-docs/rst/scala/code/docs/dispatcher/DispatcherDocSpec.scala index 7d06bb43da..49873d9737 100644 --- a/akka-docs/rst/scala/code/docs/dispatcher/DispatcherDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/dispatcher/DispatcherDocSpec.scala @@ -186,6 +186,13 @@ class DispatcherDocSpec extends AkkaSpec(DispatcherDocSpec.config) { //#defining-pinned-dispatcher } + "looking up a dispatcher" in { + //#lookup + // for use with Futures, Scheduler, etc. + implicit val executionContext = system.dispatchers.lookup("my-dispatcher") + //#lookup + } + "defining priority dispatcher" in { //#prio-dispatcher diff --git a/akka-docs/rst/scala/dispatchers.rst b/akka-docs/rst/scala/dispatchers.rst index ac95c9c42c..68182af86e 100644 --- a/akka-docs/rst/scala/dispatchers.rst +++ b/akka-docs/rst/scala/dispatchers.rst @@ -13,6 +13,15 @@ Default dispatcher Every ``ActorSystem`` will have a default dispatcher that will be used in case nothing else is configured for an ``Actor``. The default dispatcher can be configured, and is by default a ``Dispatcher`` with a "fork-join-executor", which gives excellent performance in most cases. +.. _dispatcher-lookup-scala: + +Looking up a Dispatcher +----------------------- + +Dispatchers implement the :class:`ExecutionContext` interface and can thus be used to run :class:`Future` invocations etc. + +.. includecode:: code/docs/dispatcher/DispatcherDocSpec.scala#lookup + Setting the dispatcher for an Actor ----------------------------------- diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala index c37e3e1ed8..a227611fdf 100644 --- a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala @@ -40,7 +40,7 @@ class CreationActor extends Actor { case result: MathResult ⇒ result match { case MultiplicationResult(n1, n2, r) ⇒ println("Mul result: %d * %d = %d".format(n1, n2, r)) - case DivisionResult(n1, n2, r) ⇒ + case DivisionResult(n1, n2, r) ⇒ println("Div result: %.0f / %d = %.2f".format(n1, n2, r)) } } diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala index cee40de5e7..70f49eb29d 100644 --- a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala @@ -40,7 +40,7 @@ class LookupActor extends Actor { def receive = { case (actor: ActorRef, op: MathOp) ⇒ actor ! op case result: MathResult ⇒ result match { - case AddResult(n1, n2, r) ⇒ + case AddResult(n1, n2, r) ⇒ println("Add result: %d + %d = %d".format(n1, n2, r)) case SubtractResult(n1, n2, r) ⇒ println("Sub result: %d - %d = %d".format(n1, n2, r))