Merge pull request #28496 from akka/managingBlockingExample
Update 'managing blocking' examples
This commit is contained in:
commit
cbde44fc48
3 changed files with 14 additions and 24 deletions
|
|
@ -144,7 +144,7 @@ is typically that (network) I/O occurs under the covers.
|
||||||
|
|
||||||
### Problem: Blocking on default dispatcher
|
### Problem: Blocking on default dispatcher
|
||||||
|
|
||||||
Simply add blocking calls to your actor message processing like this is problematic:
|
Simply adding blocking calls to your actor message processing like this is problematic:
|
||||||
|
|
||||||
Scala
|
Scala
|
||||||
: @@snip [BlockingDispatcherSample.scala](/akka-docs/src/test/scala/docs/actor/typed/BlockingActor.scala) { #blocking-in-actor }
|
: @@snip [BlockingDispatcherSample.scala](/akka-docs/src/test/scala/docs/actor/typed/BlockingActor.scala) { #blocking-in-actor }
|
||||||
|
|
@ -154,7 +154,7 @@ Java
|
||||||
|
|
||||||
Without any further configuration the default dispatcher runs this actor along
|
Without any further configuration the default dispatcher runs this actor along
|
||||||
with all other actors. This is very efficient when all actor message processing is
|
with all other actors. This is very efficient when all actor message processing is
|
||||||
non-blocking. If all of the available threads are blocked, however, then all the actors on the same dispatcher will starve for threads and
|
non-blocking. When all of the available threads are blocked, however, then all the actors on the same dispatcher will starve for threads and
|
||||||
will not be able to process incoming messages.
|
will not be able to process incoming messages.
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
@ -187,7 +187,7 @@ Java
|
||||||
: @@snip [BlockingDispatcherTest.java](/akka-docs/src/test/java/jdocs/actor/typed/BlockingDispatcherTest.java) { #blocking-main }
|
: @@snip [BlockingDispatcherTest.java](/akka-docs/src/test/java/jdocs/actor/typed/BlockingDispatcherTest.java) { #blocking-main }
|
||||||
|
|
||||||
|
|
||||||
Here the app is sending 100 messages to `BlockingActor` and `PrintActor` and large numbers
|
Here the app is sending 100 messages to `BlockingActor`s and `PrintActor`s and large numbers
|
||||||
of `akka.actor.default-dispatcher` threads are handling requests. When you run the above code,
|
of `akka.actor.default-dispatcher` threads are handling requests. When you run the above code,
|
||||||
you will likely to see the entire application gets stuck somewhere like this:
|
you will likely to see the entire application gets stuck somewhere like this:
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ you will likely to see the entire application gets stuck somewhere like this:
|
||||||
```
|
```
|
||||||
|
|
||||||
`PrintActor` is considered non-blocking, however it is not able to proceed with handling the remaining messages,
|
`PrintActor` is considered non-blocking, however it is not able to proceed with handling the remaining messages,
|
||||||
since all the threads are occupied and blocked by the other blocking actor - thus leading to thread starvation.
|
since all the threads are occupied and blocked by the other blocking actors - thus leading to thread starvation.
|
||||||
|
|
||||||
In the thread state diagrams below the colours have the following meaning:
|
In the thread state diagrams below the colours have the following meaning:
|
||||||
|
|
||||||
|
|
@ -223,7 +223,7 @@ and then you can apply the proposed solutions as explained below.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In the above example we put the code under load by sending hundreds of messages to the blocking actor
|
In the above example we put the code under load by sending hundreds of messages to blocking actors
|
||||||
which causes threads of the default dispatcher to be blocked.
|
which causes threads of the default dispatcher to be blocked.
|
||||||
The fork join pool based dispatcher in Akka then attempts to compensate for this blocking by adding more threads to the pool
|
The fork join pool based dispatcher in Akka then attempts to compensate for this blocking by adding more threads to the pool
|
||||||
(`default-akka.actor.default-dispatcher 18,19,20,...`).
|
(`default-akka.actor.default-dispatcher 18,19,20,...`).
|
||||||
|
|
@ -246,7 +246,7 @@ that you have not configured an explicit dispatcher for).
|
||||||
|
|
||||||
When facing this, you
|
When facing this, you
|
||||||
may be tempted to wrap the blocking call inside a `Future` and work
|
may be tempted to wrap the blocking call inside a `Future` and work
|
||||||
with that instead, but this strategy is too simple: you are quite likely to
|
with that instead, but this strategy is too simplistic: you are quite likely to
|
||||||
find bottlenecks or run out of memory or threads when the application runs
|
find bottlenecks or run out of memory or threads when the application runs
|
||||||
under increased load.
|
under increased load.
|
||||||
|
|
||||||
|
|
@ -267,7 +267,7 @@ unless you @ref:[set up a separate dispatcher for the actor](../dispatchers.md#s
|
||||||
|
|
||||||
### Solution: Dedicated dispatcher for blocking operations
|
### Solution: Dedicated dispatcher for blocking operations
|
||||||
|
|
||||||
One of the most efficient methods of isolating the blocking behavior, such that it does not impact the rest of the system,
|
An efficient method of isolating the blocking behavior, such that it does not impact the rest of the system,
|
||||||
is to prepare and use a dedicated dispatcher for all those blocking operations.
|
is to prepare and use a dedicated dispatcher for all those blocking operations.
|
||||||
This technique is often referred to as "bulk-heading" or simply "isolating blocking".
|
This technique is often referred to as "bulk-heading" or simply "isolating blocking".
|
||||||
|
|
||||||
|
|
@ -277,7 +277,6 @@ be configured as follows:
|
||||||
<!--same config text for Scala & Java-->
|
<!--same config text for Scala & Java-->
|
||||||
@@snip [BlockingDispatcherSample.scala](/akka-docs/src/test/scala/docs/actor/typed/BlockingDispatcherSample.scala) { #my-blocking-dispatcher-config }
|
@@snip [BlockingDispatcherSample.scala](/akka-docs/src/test/scala/docs/actor/typed/BlockingDispatcherSample.scala) { #my-blocking-dispatcher-config }
|
||||||
|
|
||||||
|
|
||||||
A `thread-pool-executor` based dispatcher allows us to limit the number of threads it will host,
|
A `thread-pool-executor` based dispatcher allows us to limit the number of threads it will host,
|
||||||
and this way we gain tight control over the maximum number of blocked threads the system may use.
|
and this way we gain tight control over the maximum number of blocked threads the system may use.
|
||||||
|
|
||||||
|
|
@ -296,7 +295,7 @@ The thread pool behavior is shown in the below diagram.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Messages sent to `SeparateDispatcherCompletionStageActor` and `PrintActor` are handled by the default dispatcher - the
|
Messages sent to @scala[`SeparateDispatcherFutureActor`]@java[`SeparateDispatcherCompletionStageActor`] and `PrintActor` are handled by the default dispatcher - the
|
||||||
green lines, which represent the actual execution.
|
green lines, which represent the actual execution.
|
||||||
|
|
||||||
When blocking operations are run on the `my-blocking-dispatcher`,
|
When blocking operations are run on the `my-blocking-dispatcher`,
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,9 @@ public class BlockingDispatcherTest {
|
||||||
Behavior<Void> root =
|
Behavior<Void> root =
|
||||||
Behaviors.setup(
|
Behaviors.setup(
|
||||||
context -> {
|
context -> {
|
||||||
ActorRef<Integer> actor1 = context.spawn(BlockingActor.create(), "BlockingActor");
|
|
||||||
ActorRef<Integer> actor2 = context.spawn(PrintActor.create(), "PrintActor");
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
actor1.tell(i);
|
context.spawn(BlockingActor.create(), "BlockingActor-" + i).tell(i);
|
||||||
actor2.tell(i);
|
context.spawn(PrintActor.create(), "PrintActor-" + i).tell(i);
|
||||||
}
|
}
|
||||||
return Behaviors.ignore();
|
return Behaviors.ignore();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,9 @@ object BlockingDispatcherSample {
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
// #blocking-main
|
// #blocking-main
|
||||||
val root = Behaviors.setup[Nothing] { context =>
|
val root = Behaviors.setup[Nothing] { context =>
|
||||||
val actor1 = context.spawn(BlockingFutureActor(), "futureActor")
|
|
||||||
val actor2 = context.spawn(PrintActor(), "printActor")
|
|
||||||
|
|
||||||
for (i <- 1 to 100) {
|
for (i <- 1 to 100) {
|
||||||
actor1 ! i
|
context.spawn(BlockingFutureActor(), s"futureActor-$i") ! i
|
||||||
actor2 ! i
|
context.spawn(PrintActor(), s"printActor-$i") ! i
|
||||||
}
|
}
|
||||||
Behaviors.empty
|
Behaviors.empty
|
||||||
}
|
}
|
||||||
|
|
@ -91,12 +88,9 @@ object SeparateDispatcherSample {
|
||||||
|
|
||||||
// #separate-dispatcher-main
|
// #separate-dispatcher-main
|
||||||
val root = Behaviors.setup[Nothing] { context =>
|
val root = Behaviors.setup[Nothing] { context =>
|
||||||
val actor1 = context.spawn(SeparateDispatcherFutureActor(), "futureActor")
|
|
||||||
val actor2 = context.spawn(PrintActor(), "printActor")
|
|
||||||
|
|
||||||
for (i <- 1 to 100) {
|
for (i <- 1 to 100) {
|
||||||
actor1 ! i
|
context.spawn(SeparateDispatcherFutureActor(), s"futureActor-$i") ! i
|
||||||
actor2 ! i
|
context.spawn(PrintActor(), s"printActor-$i") ! i
|
||||||
}
|
}
|
||||||
Behaviors.ignore
|
Behaviors.ignore
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue