Использование Akka со Scalatra

Моя цель — создание параллельного бэкенда для моих виджетов. В настоящее время я представляю серверную часть как веб-службу, которая получает запросы на запуск определенного виджета (используя Scalatra), извлекает код виджета из БД и запускает его в актере (используя Akka), который затем отвечает результатами. Итак, представьте, что я делаю что-то вроде:

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

Теперь я считаю, что это не лучшее параллельное решение, и я должен как-то совместить прослушивание запросов и запуск виджетов в одной реализации актора. Как бы вы спроектировали это для максимального параллелизма? Спасибо.


person parsa    schedule 10.07.2011    source источник


Ответы (1)


Вы можете запускать своих акторов в загрузочном файле akka или в своем собственном ServletContextListener, чтобы они запускались без привязки к сервлету. Затем вы можете найти их в реестре akka.

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

Кроме того, на данный момент нет реальной интеграции akka со scalatra. Так что до сих пор лучшее, что вы можете сделать, это использовать блокирующие запросы к группе актеров.

Я не уверен, но мне не нужно создавать актера для каждого запроса, а скорее иметь пул актеров-виджетов, которым вы можете отправлять эти запросы. Если вы используете иерархию супервизора, вы можете использовать супервизора для изменения размера пула, если он слишком велик или слишком мал.

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()
    } 
  }
}

Пожалуйста, рассматривайте приведенный выше код как псевдокод, который выглядит как scala, я просто хотел проиллюстрировать концепцию.

person Casual Jim    schedule 10.07.2011
comment
Спасибо за вашу помощь, в итоге я использовал интеграцию Akka Camel с конечной точкой и пулом актеров, и мне это нравится :) - person parsa; 11.07.2011
comment
Эй, Парса, ты тогда полностью отказался от Scalatra? - person Kamesh Rao Yeduvakula; 12.07.2011