I have a question regarding of blocking call handling within an actor. I have a blocking method call blokingProcess()
I want to call it within an actor. Should I have to
- Wrap this
blokingProcess()
byFuture
with a dedicateddispatcher/ExecutionContext
in an actor then usingpipeTo self
. Or - I should create a
supervisor parent actor
under which I will producechild actor
with dedicateddispatcher
. That child actor will handle the blocking callblokingProcess()
without anyFuture
wrapper .
Note I can also call blokingProcess()
by wrapping with Future
with dedicated dispatcher/ExecutionContext
with in no actor. My programme will generate so many blocking calls at a time . I don't need response back.
I know this is very silly question. But looking for experts' suggestions.
CodePudding user response:
You generally don't want to perform long-running thread-blocking work on threads of your ActorSystem
's main dispatcher pool. This is because it is that same pool that handles all the events in your application (unless specifically directed to another pool) -- so if that pool gets saturated with blocked threads, your application will deadlock.
The pattern I recommend is to set up a separate dispatcher for IO-bound blocking work. Then you can use in one of two ways, either in a single actor:
implicit val ioDispatcher: ExecutionContext = context.system.dispatchers
.lookup(DispatcherSelector.fromConfig("io-bound-dispatcher"))
val task = Future {
blocking {
// blocking work here
}
}
or in a parent-child fashion, where the IO-bound dispatcher is used for all work done by the child:
// in parent
val child = context.spawn(
childBehavior,
"child-actor-name",
DispatcherSelector.fromConfig("io-bound-dispatcher")
)
child ! msg
and have the child run the blocking action on its main thread, which will be a thread from the IO-bound dispatcher's pool.
The advantage to the second approach is it makes recovery/supervision work like it does for any other actor; in other words, if your blocking operation throws an uncaught exception, it will bubble up the supervision stack until handled. This may or may not be what you want.
In either case, it's always good to add some logging around the blocking code which will allow you to inspect the thread name that the action is occurring on. (Warning! Akka's default logging logs all events on the default thread pool; you have to explicitly request that the log event's sourceThread
from MDC is added to the log output.)
Note that all of Akka's own IO primitives (e.g. HTTP client, Kafka consumer, etc.) are designed (usually by means of the Streams APIs) to not block dispatcher threads. So you should not move these to other dispatchers unless you are very, very sure that it is they that are causing thread-pool starvation (and not merely being the victim of it).
CodePudding user response:
My answer is
- Wrap in a
Future
and useblocking
If you put blockingProcess
inside a blocking
block then the dispatcher can use a dedicated thread for that Future
and avoid the risk of deadlock.
Future{
blocking{
blockingProcess()
}
}
Using separate dispatchers or child actors feels like overkill to me.
This does depend on the particular dispatcher being used, so see here for more details.