scalaakkaakka-streamakka-http

Load Balancing akka-http


I am using akka-http, my build.sbt configuration is:

scalaVersion := "2.11.7"
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-http-experimental_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-http-spray-json-experimental_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.2"

I am exposing a simple REST API with only one GET URL
foo is a function that returns a Future

implicit val actorSystem = ActorSystem("system", config)
implicit val actorMaterializer = ActorMaterializer()

val route: Route = {
  get {
    path("foo") {
      complete { foo }
    }
  }
}

The web-service is expected to have a lot of calls, and I want to make the service redundant in case of failure, so I want to have two instances running simultaneously to handle all the requests.

  1. What is the best way to have two instances of the web-service handling the requests simultaneously?, with an external load balancer or with some magic (which I don't know) inside akka/akka-http?
  2. What are the principal parameters I have to tune up to improve perfomance?

Solution

  • The answer to this question demonstrates how to make an Actor call from within a Route.

    If you combine that technique with the clustering capabilities within Akka you should be able to get the job done.

    Have your Route send a message to a Router that will dispatch the message to 1 of N remotely deployed Actors (from your question it sounds like a round robin router is what you want).

    class HttpResponseActor extends Actor {
    
      def foo : HttpResponse = ??? // Not specified in question
    
      override def receive = {
        case _ : HttpRequest => foo 
      }
    }
    
    import akka.actor.{ Address, AddressFromURIString }
    import akka.remote.routing.RemoteRouterConfig
    
    val addresses = Seq(Address("akka.tcp", "remotesys", "otherhost", 1234),
                        AddressFromURIString("akka.tcp://othersys@anotherhost:1234"))
    
    val routerRemote = 
      system.actorOf(RemoteRouterConfig(RoundRobinPool(5), addresses).props(Props[HttpResponseActor]))
    

    The remote Actor responds with the HttpResponse. This Response can go through the Router or directly back to the Route.

    The Route sticks the answer in a complete Directive to return back to the client.

    val route = 
      get {
        path("foo") {
          onComplete((routerRemote ? request).mapTo[HttpResponse]) {
            case Success(response) => complete(response)
            case Failure(ex) => complete((InternalServerError, s"Actor not playing nice: ${ex.getMessage}"))
          }
        }
      }