Reputation: 3188
Question from an akka newbie: let's say that at some point one of my actors wants to issue an HTTP request against an external REST API. What is the best way to do it? (note: I would ask the same question about an actor wishing to store data in a RDBMS).
Should I create another type of actor for that, and create a pool of such agents. Should I then create a message type that has the semantics of "please make an HTTP call to this endpoint", and should my first actor send this message to the pool to delegate the work?
Is that the recommended pattern (rather than doing the work in the initial actor)? And if so, would I then create a message type to communicate the outcome of the request to the initial actor when it is available?
Thank you for your feedback!
Olivier
Upvotes: 3
Views: 1746
Reputation: 24040
Yes that sounds like a good approach.
If your HTTP client is blocking you will want to run the REST API calls in a different thread pool so you don't block your actors. You can use Future
in actors to avoid blocking. Using a pool of actors is also possible though it's a little more work to set up.
For example, at the top level of your application create a ExecutionContext
that is passed to actors that you create:
implicit val blockingEc =
ExecutionService.fromExecutorService(Executors.newFixedThreadPool(BlockingPoolSize))
class MyActor(implicit blockingEc: ExecutionContext) extends Actor {
def receive = {
case RestCall(arg) =>
val snd = sender()
Future { restApiCall(arg) } pipeTo snd
}
}
This will run the blocking call and send the result back to the requestor. Make sure to handle Status.Failure(ex)
messages in the calling actor in case the restApiCall
threw an exception.
The specific type and size of thread pool really depends on your application.
Upvotes: 0
Reputation: 6852
This question is old now, but presumably one of your goals is to write reactive code that does not block threads, as sourcedelica mentioned. The Spray folks are best known for their async HTTP server and their awesome routing DSL (which you would use to create your own API), but they also offer a Spray-client package that allows your app to access other servers. It is based on Futures and thus allows you to get things done without blocking. Filip Andersson wrote an illustrative example; here are a few lines from it that will give you the idea:
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
// create a function to send a GET request and receive a string response
def get(url: String): Future[String] = {
val futureResponse = pipeline(Get(url))
futureResponse.map(_.entity.asString)
}
If you are familiar with futures, you know how you can further operate on Futures without blocking (like that map
invocation). Spray's client library uses the same underlying data structures and concepts as their server side, which is handy if you are going to do both in one app.
Upvotes: 1