I have a scenario where an important actor needs to make a call to a slow (15 - 20 seconds) remote system:
// Non-actor code equivalent
public Result makeSlowNetworkCall(Request request) {
Result result = slowServiceClient.soooooSlow(request); // Could be up to 15 - 20 SECONDS (mehhhh)
return result;
}
The Akka equivalent to this is currently looking like:
// Groovy
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Result result = makeSlowNetworkCall(request)
// ...now do something with result, some 15 seconds later
}
}
Result makeSlowNetworkCall(Request request) {
slowServiceClient.soooooSlow(request)
}
}
Obviously this is blocking and bad, bad, bad. After reading this excellent article on handling non-blocking DB calls, my main takeaway is that there are essentially two "bulkheading" strategies I can employ:
- Place all
SlowServiceActor
instances in their own dispatcher, to isolate their latency/blocking-ness from other actors/threads that don't interact directly with the Slow Service; and - Invoke the Slow Service via
Futures
for true "asynchronicity"
So my best attempt thus far is:
// In application.conf:
slowServiceDispatcher {
...config here
}
class CallSlowService implements Callable<Result> {
@Override
Result call() throws Exception {
slowServiceClient.soooooSlow(request)
}
}
// Created using the "slowServiceDispatcher"
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Future<Result> callSlowServiceFuture = Futures.future(new CallSlowService())
Result result = ???
// ...now do something with result, some 15 seconds later
}
}
}
But as you can see, I have a few problems:
- I think I am misunderstanding the
Futures.future(...)
API; I don't think that's meant for constructing newFutures
- How do I actually obtain the
result
in a non-blocking fashion? - And finally: am I missing anything here? Any strategies I'm not utilizing/leveraging that I should be?
If I understand this correctly, you kind of have two options here: you listen to a
Future
being completed or you do something with the result:If you want to listen, you can use some callback like
The other way would be to
map
the future's result. So you have the something like:This way you say "... the moment I get a result from the service call, please manipulate it as described in apply ..."