2016-05-16 6 views
2

Ich verwende die async Play WS Scala API, um einen RESTful Service abzufragen. Ich frage mich, wie ich eine List mit Request-URLs verarbeitet werden könnte über WSClient, aber nicht mehr als eine Anfrage pro Sekunde (der Dienst erlaubt "nur" 1 Anfrage pro Sekunde pro Client). Aus einer logischen Sicht besteht die Idee darin, ein Element (URL) von der Liste zu erhalten, eine Anfrage zu machen und dann eine gewisse Zeit zu warten, bevor mit dem nächsten Element in der Liste fortgefahren wird.Play WS API: Throttling Request Preise

  • mit guten alten Thread.sleep in einem nicht blockierenden und asynchronen Rahmen wie Play ist sicherlich eine schlechte Idee.
  • das gleiche gilt wahrscheinlich für Dinge wie ScheduledThreadPoolExecutor oder andere Methoden, die neue Threads erzeugen müssen.
  • Wie könnte ich die Anfragerate drosseln, ohne negative Auswirkungen auf die asynchrone und "so wenig Threads wie möglich" Art von Play zu haben?

    +0

    Was of Play Version:


    Im Play 2.4.x und früher, könnten Sie die gleiche Promise.timeout mit tun? –

    +0

    Spielversion 2.5.3 – ceran

    Antwort

    3

    Angenommen, Sie eine Liste von URLs haben Sie zu holen sehen:

    val urls = List(
        "http://www.google.com", 
        "http://stackoverflow.com", 
        "http://www.bing.com" 
    ) 
    

    Im Play 2.5.x wir diese nacheinander verarbeiten kann, und verwenden Sie akka.pattern.after zwischen jedem Aufruf einer asynchronen Verzögerung zu erzwingen. Wir flatMap das Future Ergebnis eines Webservice Aufruf an etwas, das den gleichen Wert nach einer Sekunde zurückgibt.

    Future.traverse(urls) { url => 
        wsClient.url(url).get().flatMap { result => 
        // potentially process `result` here 
        akka.pattern.after(1.second, actorSystem.scheduler)(Future.successful(result)) 
        } 
    } // returns Future[List[WSResponse]] 
    

    Dies erfordert, dass Sie eine WSClient und ActorSystem Komponente zur Verfügung haben, sowie in impliziten ExecutionContext Umfang.

    Future.traverse(urls) { url => 
        wsClient.url(url).get().flatMap { result => 
        // potentially process `result` here 
        Promise.timeout(result, 1.second) 
        akka.pattern.after(1.second, actorSystem.scheduler)(Future.successful(result)) 
        } 
    } 
    
    1

    Akka hat eine handliche Scheduler Funktionalität hier: http://doc.akka.io/docs/akka/current/scala/scheduler.html

    Da Akka bereits im Spiel ist, brauchen Sie nicht, etwas anderes zu importieren.
    Es wäre nicht das sauberste oder leicht überprüfbar sein, aber man könnte so etwas wie:

    val webserviceCall : Runnable = new Runnable { 
    
        override def run(): Unit = { 
         // do webservice call work 
         // figure out if you need to make more webservice calls, and if you do: 
         actorSystem.scheduler.scheduleOnce(0 seconds, 1 seconds, webserviceCall) 
        } 
    
    } 
    
    actorSystem.scheduler.scheduleOnce(0 seconds, webserviceCall) 
    

    Alternativ können Sie diese Akka Nachricht throttler verwenden, die jemand vor einer Weile gemacht: http://doc.akka.io/docs/akka/snapshot/contrib/throttle.html

    ich es verwendet haben vorher (ich glaube es war letztes Jahr Akka 2.3) aber ich bin mir nicht sicher, ob es noch funktionieren wird.