4
votes

I have a layered architecture in a Java web application. The UI layer is just Java, services are typed Akka actors and external service calls (WS, DB etc.) are wrapped in Hystrix commands.

THe UI calls the service and the service returns an Akka future. It's an Akka future because I want to make UI coding simpler with the onComplete and onFailure callbacks that Akka futures provide. The service then creates the future that does some mapping etc. and wraps a call to a HystrixCommand that returns a Java future.

So in pseudocode:

UI

AkkaFuture future = service.getSomeData();

Service

public AkkaFuture getSomeData() {
    return future {
        JavaFuture future = new HystrixCommand(mapSomeData()).queue()
        //what to do here, currently just return future.get()
    }
}

The problem is that I would like to free up the thread the service actor is using and just tie up the threads that Hystrix uses. But the java future prevents that because I have to block on it's completion. The only option I can think of (which I'm not sure I like) is to poll the Java future(s) constantly and complete the Akka future when the Java future finishes.

Note: the question isn't really related to Hystrix per se, but I decided to mention it if somebody comes up with a solution specifically related to Hystrix.

3

3 Answers

2
votes

Java futures are known to be inferior in design compared to something like Scala futures. Take a look at the discussion "How do I wrap a java.util.concurrent.Future in an Akka Future", for example.

But: Maybe, instead of polling (as suggested in the above discussion), Hystrix offers some kind of onComplete callback? I do not know the library at all but stumbled upon an onComplete in the Hystrix API. Maybe it helps?

3
votes

I'm marking the answer by @Hbf as a solution, since I ended up doing an Akka poller as explained in How do I wrap a java.util.concurrent.Future in an Akka Future?. For reference I also tried:

  • Creating a HystrixCommandExcutionHook and extending HystrixCommand to allow callbacks. That didn't work because the hook wasn't called at the right time.
  • Using Guavas listenable future by having a decorated executor create the futures inside Hystrix and then casting the futures from the commands. Doesn't work because Hystrix uses a ThreadPoolExecutor which can't be decorated.

EDIT: I'm adding the Akka poller code below, since the original answer was in Scala and it hangs if the Java future doesn't cancel nicely. The solution below always walks away from threads after a timeout.


    protected  Future wrapJavaFutureInAkkaFuture(final java.util.concurrent.Future javaFuture, final Option maybeTimeout, final ActorSystem actorSystem) {
      final Promise promise = Futures.promise();
        if (maybeTimeout.isDefined()) {
          pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, Option.option(maybeTimeout.get().fromNow()), actorSystem);
        } else {
          pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, Option. none(), actorSystem);
        }

        return promise.future();
    }

    protected  void pollJavaFutureUntilDoneOrCancelled(final java.util.concurrent.Future javaFuture, final Promise promise, final Option maybeTimeout, final ActorSystem actorSystem) {
      if (maybeTimeout.isDefined() && maybeTimeout.get().isOverdue()) {
        // on timeouts, try to cancel the Java future and simply walk away
        javaFuture.cancel(true);
        promise.failure(new ExecutionException(new TimeoutException("Future timed out after " + maybeTimeout.get())));

      } else if (javaFuture.isDone()) {
        try {
          promise.success(javaFuture.get());
        } catch (final Exception e) {
          promise.failure(e);
        }
      } else {
            actorSystem.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS), new Runnable() {
          @Override
          public void run() {
            pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, maybeTimeout, actorSystem);
          }
        }, actorSystem.dispatcher());
      }
    }
2
votes

As of Hystrix 1.3 it now also supports true non-blocking callbacks and that will fit much better into Akka/Scala Future behavior that is non-blocking and composable: https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Reactive-Execution