4

My target is building a highly concurrent backend for my widgets. I'm currently exposing the backend as a web service, which receives requests to run a specific widget (using Scalatra), fetches widget's code from DB and runs it in an actor (using Akka) which then replies with the results. So imagine I'm doing something like:

get("/run:id") {
  ...
  val actor = Actor.actorOf("...").start
  val result = actor !! (("Run",id), 10000)
  ...
} 

Now I believe this is not the best concurrent solution and I should somehow combine listening for requests and running widgets in one actor implementation. How would you design this for maximum concurrency? Thanks.

parsa
  • 2,628
  • 3
  • 34
  • 44

1 Answers1

5

You can start your actors in an akka boot file or in your own ServletContextListener so that they are started without being tied to a servlet. Then you can look for them with the akka registry.

Actor.registry.actorFor[MyActor] foreach { _ !! (("Run",id), 10000) }

Apart from that there is no real integration for akka with scalatra at this moment. So until now the best you can do is by using blocking requests to a bunch of actors.

I'm not sure but I wouldn't necessary spawn an actor for each request but rather have a pool of widget actors which you can send those requests. If you use a supervisor hierarchy then the you can use a supervisor to resize the pool if it is too big or too small.

class MyContextListener extends ServletContextListener {

  def contextInitialized(sce: ServletContextEvent) {
    val factory = SupervisorFactory(
      SupervisorConfig(
      OneForOneStrategy(List(classOf[Exception]), 3, 1000),
      Supervise(actorOf[WidgetPoolSupervisor], Permanent)
  }

  def contextDestroyed(sce: ServletContextEvent) {
    Actor.registry.shutdownAll()
  }
}

class WidgetPoolSupervisor extends Actor {

  self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 3, 1000)

  override def preStart() {
    (1 to 5) foreach { _ =>
       self.spawnLink[MyWidgetProcessor]
    }
    Scheduler.schedule(self, 'checkPoolSize, 5, 5, TimeUnit.MINUTES)
  }

  protected def receive = {
    case 'checkPoolSize => {
      //implement logic that checks how quick the actors respond and if 
      //it takes to long add some actors to the pool.
      //as a bonus you can keep downsizing the actor pool until it reaches 1
      //or until the message starts returning too late.
    }
  }
}

class ScalatraApp extends ScalatraServlet {

  get("/run/:id") {
    // the !! construct should not appear anywhere else in your code except
    // in the scalatra action. You don't want to block anywhere else, but in a 
    // scalatra action it's ok as the web request itself is synchronous too and needs to 
    // to wait for the full response to have come back anyway.
    Actor.registry.actorFor[MyWidgetProcessor] foreach { 
      _ !! ((Run, id), 10000) 
    } getOrElse {
      throw new HeyIExpectedAResultException()
    } 
  }
}

Please do regard the code above as pseudo code that happens to look like scala, I just wanted to illustrate the concept.

Casual Jim
  • 1,179
  • 7
  • 13