7

I have the following requirement

  • Connect to a webserver with a username and password and get an authetication token
  • Read file to get different parameters
  • Use the auth token fro step 1 and parameters from step 2 to send an http request to the web server

Right now I have a single actor that carries out all of the above tasks as follows

package akka.first.java;

import akka.actor.UntypedActor;

public class MySingleActor extends UntypedActor {

    public void onReceive(Object msg) {

        if( msg instanceof sendRequest ) {

            //Connect to a webserver with a username and password and get an authetication token
            String token = getToken();
           // Read file to get different parameters
            Param param = readFile();
           // Use the auth token fro step 1 and parameters from step 2 to send an http request to the web server
            Response response = sendRequest (server, token, param);


        }

    }

    private Param readFile() {
        // reads file 
    }

    private String getToken() {
        //gets token 
    }
}

The readFile operation contains various subtasks that I think it should be a separate actor. But since the return from the readFile() operation is required for the actor to perform its main task of sending the request, this might be blocking which according to the docs is not recommended, what is the best way to go about this? Futures?

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
user_mda
  • 18,148
  • 27
  • 82
  • 145

2 Answers2

5

Official documentation offers following solutions:

  • Do the blocking call within an actor (or a set of actors managed by a router [Java, Scala]), making sure to configure a thread pool which is either dedicated for this purpose or sufficiently sized.
  • Do the blocking call within a 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 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.

Using the futures is among the officially suggested approaches, however with extra care.

Let's consider the first approach because IMO it is more consistent.

First of all extract all the blocking IO operations into new actors that are performing only one blocking IO operation. Assume that there is only one such operation for brevity:

public class MyBlockingIOActor extends UntypedActor {
    public void onReceive(Object msg) {
        // do blocking IO call here and send the result back to sender
    }
}

Add configuration for dispatcher, that will take care of blocking actors, in actor system configuration file (usually application.conf):

#Configuring a dispatcher with fixed thread pool size, e.g. for actors that perform blocking IO
blocking-io-dispatcher {
  type = Dispatcher
  executor = "thread-pool-executor"
  thread-pool-executor {
    fixed-pool-size = 32
  }
  throughput = 1
}

Please, make sure that you use the configuration file when creating the actor system (especially if you decided to use non-standard file name for the configuration):

ActorSystem actorSystem = ActorSystem.create("my-actor-system", ConfigFactory.load("application.conf"));

After that you want to assign the actor which performs blocking IO to dedicated dispatcher. You can do it in the configuration as described here or when creating the actor:

ActorRef blockingActor = context().actorOf(Props.create(MyBlockingIOActor.class).withDispatcher("blocking-io-dispatcher"));

In order to get more throughput, consider wrapping blocking actor into pool:

SupervisorStrategy strategy = new OneForOneStrategy(
        5,
        Duration.create(1, TimeUnit.MINUTES),
        Collections.singletonList(Exception.class)
);
ActorRef blockingActor = context().actorOf(new SmallestMailboxPool(5).withSupervisorStrategy(strategy).props(Props.create(MyBlockingIOActor.class).withDispatcher("blocking-io-dispatcher")));

You can ensure that the actor uses the right dispatcher in the following way:

public class MyBlockingIOActor extends UntypedActor {
    public void preStart() {
        LOGGER.debug("using dispatcher: {}", ((Dispatcher)context().dispatcher()).id());
    }
}
Oleg Kurbatov
  • 1,376
  • 1
  • 19
  • 32
2

You can use Futures,or maybe RxJava with Observables and Observers. Or different actors and forward the final response to the orginial sender

  public class MySingleActor extends UntypedActor{

private ActorRef tokenActor;
private ActorRef readFileActor;

public MySingleActor(){
    tokenActor = context().actorOf(Props.create(TokenActor.class),"tokenActor");
    readFileActor = context().actorOf(Props.create(ReadFileActor.class),"readFileActor");
}
public void onReceive(Object msg) {
    if( msg instanceof sendRequest ) {
        Future<String> f= Futures.future(new Callable<String>() {
            @Override public String call() throws Exception {
                return getToken();
            }            },context().dispatcher());Patterns.pipe(f,context().dispatcher()).to(tokenActor).pipeTo(readFileActor,self());
    }       
}}

Or instead of pipe

f.onComplete(new OnComplete<String>(){ public void onComplete(Throwable t, String result){ readFileActor.tell(result,self()); } }, context().system().dispatcher());

gaston
  • 498
  • 7
  • 15
  • do you mean akka futures? IF I forward the response wouldnt it be blocking until the response is received? – user_mda Sep 14 '16 at 16:02
  • In readfile actor, use scala futures, and onsuccess forward the response to the sender – gaston Sep 14 '16 at 16:55
  • I am still unclear, can you add more context in terms of how the code would look like? The futures would be placeholders for the data the actor fetches from outside the actor system correct and this future is rturned to the 'MySingleActor actor as a forwarded msg? – user_mda Sep 14 '16 at 16:59
  • In MySingleActor, create generateToken Actor, and with Patterns.ask(generateToken, MESSAGE, timeout in millis), obtain a future (in MESSAGE object send the sender() has a parameter), add onSuccess method to future anda call nextActor,readFile, and do the the same in readFileActor with futures. To send the final response,tell to the original sender, passed through the MESSAGE object, the response. see http://doc.akka.io/docs/akka/2.4/java/futures.html – gaston Sep 15 '16 at 12:50
  • Isnt this a blocking call thats discouraged? – user_mda Sep 16 '16 at 13:15