I've a Spray client that is used to invoke a REST web service. Currently I'm creating an instance of this client (using new GeoSprayWebClient
) [see code below] and reusing it inside my actors to make the REST requests. However, a single instance of the service is not able to handle all the load. Therefore, I want to introduce replicas of the REST service.
I'm new to Spray and still trying to learn the fundamentals. My questions are
1) I know Spray internally uses Akka actors. In this particular case can I get an ActorRef
for client instances so that I can create multiple client ActorRefs and use them to create an Akka router.
2) Does the Spray client API provide any kind of routing capability that will support my use case?
import akka.actor.ActorSystem
import spray.client.pipelining._
import spray.http._
import scala.concurrent.Future
trait GeoWebClient {
def get(url: String, params: Map[String, String]): Future[String]
}
class GeoSprayWebClient(implicit system: ActorSystem) extends GeoWebClient {
import system.dispatcher
// create a function from HttpRequest to a Future of HttpResponse
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
// create a function to send a GET request and receive a string response
def get(path: String, params: Map[String, String]): Future[String] = {
val uri = Uri(path) withQuery params
val request = Get(uri)
val futureResponse = pipeline(request)
futureResponse.map(_.entity.asString)
}
}
Based on this I was able to get the ActorRef
def createHttpRESTClient(host: String, port: Int): ActorRef = {
// execution context for future transformations below
import system.dispatcher
implicit val timeout: Timeout = 10 seconds
val ref: Future[ActorRef] = for {
Http.HostConnectorInfo(hostConnector: ActorRef, _) <- IO(Http) ? Http.HostConnectorSetup(host, port)
}
yield {
hostConnector
}
//FIXME - TODO fix this it's really bad. However, We are going to create this only once when we create the actor, so I guess it's okay for now.
Await.result(ref, 10 seconds)
}
And this is how I'm sending the request and getting a response from the service using the ActorRef.
def sendReq(text: String): Future[String] = {
import spray.http._
val params = Map(("key" -> text))
val uri = Uri("/myservice") withQuery params
val request = Get(uri)
//send GET request using the "ask" pattern; the timeout
//TODO - not sure if we can use tell instead of ask here ?
val response: Future[HttpResponse] = restSvrActorRef.ask(request).mapTo[HttpResponse]
log.debug(s"done with sending a request to the REST service")
response.map(_.entity.asString)
}