Calling an Actor in a Spray route and waiting for the Actor's response
Asked Answered
S

2

5

After have used Play! Framework for a while, I'm taking a first look to Spray. I started from a sample I found on GitHub, now I want to modify it but it's not easy for me to get how it works.

How can I wait for a message from an Actor in the code below?

package api

import akka.actor.ActorRef
import scala.concurrent.ExecutionContext
import spray.routing.Directives
import core.ClassifierActor

class ClassifierService(classifier: ActorRef)(implicit executionContext: ExecutionContext)
  extends Directives with DefaultJsonFormats {

  import ClassifierActor._

  implicit val classifyMessageFormat = jsonFormat4(ClassifyMessage)

  val route =
    path("classify") {
      post {
        handleWith { 
          // The ClassifierActor gets a ClassifyMessage and 
          // sends a ClassifiedMessage back to the sender.
          // How can wait for the ClassifiedMessage here 
          // and send a HttpResponse back?
          cm: ClassifyMessage => classifier ! cm
          // ???
        }
      }
    }

}
Scherman answered 11/10, 2014 at 11:0 Comment(2)
I didn't write our routing code so I don't know the best way but broadly you never wait: instead you hand the request over to an actor or perhaps an actor wrapped in a future via a tell that is responsible for sending the reply once the processing is complete.Kandi
I'm new to Spray, so I cannot get any advantage from your reply. I'd rather need a snippet of code.Scherman
T
13

Spray is already based on akka.io

Thus, if you want just to complete your route with actor response, you could use ask pattern

import akka.pattern.ask  
import scala.concurrent.duration._
implicit val timeout = Timeout(5 seconds) // needed for `?` below

 val route =
    path("classify") {
      post {
        onComplete(actorResponse(yourActor, yourMessage)) {
          complete(_)
        }
      }
    }

def actorResponse[T](actor: ActorRef, msg: ClassifyMessage): Future[T] = 
(actor ? msg).mapTo[T]

If you want to forward request to your actor model and complete route somewhere in actor system, you need to forward RequestContext to actors. Maybe, this example could help you. Good luck!

Tal answered 11/10, 2014 at 15:28 Comment(2)
Thanks! You might add 'import scala.concurrent.Future' since also Java has a future. And 'import akka.actor.Actor' just because also it is needed.Woolfolk
One important note: the person is already using actors (see the "classifier: ActorRef" in the q). If one is simply looking for a way to multithread response logic, Future[HttpResponse] is the way to go, not actors. See chrisstucchio.com/blog/2013/actors_vs_futures.htmlWoolfolk
J
3

Take a look at my example project. This service uses Futures to complete routes. Like Rup commented, it's bad practice to wait for a response. Return a future immediately and let it complete when it gets a result.

In your example classifier ! cm is using the actor "tell" pattern. It sends a message cm to the classifier actor and moves on. If you want it to expect a response back in a future use the "ask" pattern: classifier ? cm. In your cm actor's receive method you will return a future with sender ! responseMsg which will return in a future.

Junkie answered 11/10, 2014 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.