12

大規模な Scala + Akka + PlayMini アプリケーションを外部 REST API と接続しようとしています。ルート URL を定期的に (基本的には 1 ~ 10 分ごとに) ポーリングし、サブレベルの URL をクロールしてデータを抽出し、メッセージ キューに送信するという考え方です。

これを行うには、次の 2 つの方法を考え出しました。

第1の方法

API のリソース パス構造に一致するアクターの階層を作成します。Google Latitude の場合、たとえば次のようになります。

この場合、各アクターは、関連するリソースを定期的にポーリングし、次のレベルのパス リソースの子アクターを作成/削除します (つまり、アクター 'latitude/v1/location' は、アクター 1、2、3 などを作成します)。https://www.googleapis.com/latitude/v1/locationのポーリングを通じて学習したすべての場所)。

2番目の方法

ルーターによって負荷分散されたポーリング要求 (リソース パスを含む) を受信する同一のポーリング アクターのプールを作成し、URL を 1 回ポーリングし、何らかの処理を行い、ポーリング要求をスケジュールします (次のレベルのリソースとポーリングされた URL の両方に対して)。 . Google Latitude では、たとえば次のようになります。

1 ルーター、n ポーラー アクター。https://www.googleapis.com/latitude/v1/locationに対する最初のポーリング リクエストは、 https://www.googleapis.com/latitude/v1/location/1https:/に対するいくつかの新しい (即時の) ポーリング リクエストにつながります。 /www.googleapis.com/latitude/v1/location/2など、および同じリソースに対する 1 つの (遅延した) ポーリング リクエスト、つまりhttps://www.googleapis.com/latitude/v1/location

私は両方のソリューションを実装しましたが、少なくとも私が興味を持っている API とポーリング頻度については、関連するパフォーマンスの違いをすぐに観察することはできません。 .scheduler.schedule(...) 2 番目のアプローチ (ここでは、scheduleOnce(...) が必要) よりも。また、リソースが複数のレベルでネストされており、存続期間がやや短い (たとえば、各ポーリング間で複数のリソースが追加/削除される可能性がある) と仮定すると、akka のライフサイクル管理により、最初のケースでブランチ全体を簡単に強制終了できます。2 番目のアプローチは (理論的には) 高速であり、コードの記述もやや簡単です。

私の質問は次のとおりです。

  1. (パフォーマンス、拡張性、コードの複雑さなどの点で) どのアプローチが最善と思われますか?
  2. どちらかのアプローチ (特に最初のもの) の設計に何か問題があると思いますか?
  3. 誰かが似たようなものを実装しようとしましたか? それはどのように行われましたか?

ありがとう!

4

1 に答える 1

1

マスター ポーラーを作成して、スケジュールに従って非同期リソース リクエストを開始してみませんか?

私は Akka の専門家ではありませんが、これを試してみました。

フェッチするリソースのリストを反復処理するポーラー オブジェクト:

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
    })
  }
}
于 2012-09-09T09:59:43.960 に答える