Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka HTTP Code/Structural Patterns

I am just starting with Akka Http (and Scala) and was wondering if there are any well-defined patterns for structuring Akka code. In particular, I am looking for structural patterns for composing/aggregating routes dynamically. In particular, I'm looking for a solution similar to the below pseudocode:

trait MyActor extends Actor {
  val myRouter: ActorRef = context.actorOf(FromConfig.props(Props[MyWorker]), "Worker")
val myRoute = .... //route definition for this trait
}



trait MySecondActor extends Actor {
  val mySecondRouter: ActorRef = context.actorOf(FromConfig.props(Props[MySecondWorker]), "SecondWorker")
    val myRoute = .... //route definition for this trait
}

Then, in my main server just mix in the traits to get both actors and routes automagically:

class HttpServer extends SomeTrait with MyActor with MySecondActor {
.....
.....
 }

There are some obvious problems with the above pattern including:

  1. All traits inherit from Actor and then mix into the same class - not sure what the final behaviour will be.
  2. The HttpServer class itself becomes an actor and cannot be instantiated by new HttpServer()
  3. The routes are not concatinated.

What I'm looking for is a pattern that:

  1. Allows me to separate routing logic in various packages and preferable bundle it with corresponding actors.
  2. Make routes dynamic and discoverable instead of specifying them at boot time.

I came across the following two on StackOverflow but was wondering if there's a better approach and a well-defined pattern:

  1. akka-http with multiple route configurations (This isn't really dynamic)

  2. How to aggregate akka-http routes using a trait? (Old question using reflection)

Thanks!

like image 816
MojoJojo Avatar asked Mar 10 '23 17:03

MojoJojo


1 Answers

When using akka-http you usually don't need actors to implement the routing. Actors are usually only used to access the business logic asynchronously. Let me give you an example on how to structure your code with predefined routes. This is the pattern I learned from Heiko Seeberger.

First create an object creating your routes:

object Api {

  def route: Route = {
    import import akka.http.scaladsl.server.Directives._

    pathSingleSlash {
      get {
        complete(StatusCodes.OK)
      }
    }
  }
}

If you need an actor to access your business logic you can pass it as parameter to the route method. Remember to use the ask pattern, when interacting with actor during request handling.

Than you create a Root actor which creates your Api:

final class Root extends Actor with ActorLogging {

  Http(context.system)
    .bindAndHandle(Api.route, "0.0.0.0", 8000)
    .pipeTo(self)

  override def receive: Receive = {
    case s: ServerBinding =>
      log.info(s"Listening to ${s.localAddress}")
      context.become(Actor.emptyBehavior)
    case Status.Failure(t) =>
      log.error(t, "Error binding to network interface")
      context.stop(self)
  }
}

Finally you need some main method to create the actor system and the Root actor:

object Main {

  def main(args: Array[String]): Unit = {
    val system = ActorSystem("user-api")
    system.actorOf(Props(new Root))

    Await.ready(system.whenTerminated, Duration.Inf)
  }
}

So this would be my take on best practices for defining akka-http routes. What is not covered in my answer is how to dynamically discover routes, but to be honest, I fail to see the use case here. Usually your system should have some well defined end points. How would users know which endpoints they can talk to if even the system does not know which endpoints it will serve at startup time?

like image 101
britter Avatar answered Mar 20 '23 14:03

britter