Использование Акки со Скалатрой - PullRequest
4 голосов
/ 10 июля 2011

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

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

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

1 Ответ

5 голосов
/ 10 июля 2011

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

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

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

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

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Я просто хотел проиллюстрировать концепцию.

...