Akka для опроса REST

Я пытаюсь связать большое приложение Scala + Akka + PlayMini с внешним API REST. Идея заключается в периодическом опросе (в основном каждые 1-10 минут) корневого URL-адреса и последующем обходе через URL-адреса суб-уровня для извлечения данных, которые затем отправляются в очередь сообщений.

У меня есть два способа сделать это:

1-й способ

Создайте иерархию участников для соответствия структуре пути ресурса API. В случае с Google Latitude это означает, например,

В этом случае каждый участник отвечает за периодический опрос связанного с ним ресурса, а также создание/удаление дочерних участников для ресурсов пути следующего уровня (например, "широта/v1/location" актера создает участников 1, 2, 3, и т.д. для всех местоположений, о которых он узнает, путем опроса https://www.googleapis.com/latitude/v1/location).

Второй способ

Создайте пул идентичных субъектов опроса, которые получают запросы на опросы (содержащие путь к ресурсу), сбалансированные по нагрузке маршрутизатором, один раз опроса URL, выполняют некоторую обработку и рассылают запросы на опрос (как для ресурсов следующего уровня, так и для опросный URL). В Google Локаторе это будет означать, например:

1 роутер, n игроков-поллеров. Первоначальный запрос на опрос https://www.googleapis.com/latitude/v1/location приводит к нескольким новым (немедленным) запросам опроса для https://www.googleapis.com/latitude/v1/location/1, https://www.googleapis.com/latitude/v1/location/2 и т.д. и один (отложенный) запрос опроса для того же ресурса, то есть https://www.googleapis.com/latitude/v1/location.

Я реализовал оба решения и не могу сразу заметить какую-либо существенную разницу в производительности, по крайней мере, не для интересующих меня API и частоты опроса. Я считаю, что первый подход будет несколько проще рассуждать и, используйте с system.scheduler.schedule(...), чем второй подход (где мне нужно запланироватьOnce (...)). Кроме того, предполагая, что ресурсы вложены через несколько уровней и несколько недолговечны (например, несколько ресурсов могут быть добавлены/удалены между каждым опросом), управление жизненным циклом akka позволяет легко убить целую ветвь в первом случае. Второй подход должен (теоретически) быть более быстрым, а код несколько легче писать.

Мои вопросы:

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

Спасибо!

Ответ 1

Почему бы не создать мастер-опрос, который затем запускает асинхронные запросы ресурсов в расписании?

Я не эксперт, использующий Akka, но я сделал это:

Объект poller, который выполняет итерацию через список ресурсов для извлечения:

import akka.util.duration._
import akka.actor._
import play.api.Play.current
import play.api.libs.concurrent.Akka

object Poller {
  val poller = Akka.system.actorOf(Props(new Actor {
    def receive = {
      case x: String => Akka.system.actorOf(Props[ActingSpider], name=x.filter(_.isLetterOrDigit)) ! x
    }
  }))

  def start(l: List[String]): List[Cancellable] =
    l.map(Akka.system.scheduler.schedule(3 seconds, 3 seconds, poller, _))

  def stop(c: Cancellable) {c.cancel()}
}

Актер, который читает ресурс асинхронно и запускает более асинхронные чтения. Вы можете отправить рассылку сообщения по расписанию, а не сразу звонить, если он был добрее:

import akka.actor.{Props, Actor}
import java.io.File

class ActingSpider extends Actor {
  import context._
  def receive = {
    case name: String => {
      println("reading " + name)
      new File(name) match {
        case f if f.exists() => spider(f)
        case _ => println("File not found")
      }
      context.stop(self)
    }
  }

  def spider(file: File) {
    io.Source.fromFile(file).getLines().foreach(l => {
      val k = actorOf(Props[ActingSpider], name=l.filter(_.isLetterOrDigit))
      k ! l
    })
  }
}