2014-10-27 7 views
7

ich die folgenden POC versucht, zu überprüfen, wie hohe ParallelitätWie erreichen Sie hohe Gleichzeitigkeit mit spray.io in diesem Future und Thread.sleep Beispiel?

implicit def executionContext = context.system.dispatchers.lookup("async-futures-dispatcher") 
    implicit val timeout = 10 seconds 


    val contestroute = "/contestroute" { 
     get { 
      respondWithMediaType(`application/json`) { 
      dynamic { 
       onSuccess(
        Future { 
        val start = System.currentTimeMillis() 
        // np here should be dealt by 200 threads defined below, so why 
        // overall time takes so long? why doesn't it really utilize all 
        // threads I have given to it? how to update the code so it 
        // utilizes the 200 threads? 
        Thread.sleep(5000) 
        val status = s"timediff ${System.currentTimeMillis() - start}ms ${Thread.currentThread().getName}" 
        status 
        }) { time => 
        complete(s"status: $time") 
       } 
      } 
      } 
     } 
    } 

Meine Config zu bekommen:

async-futures-dispatcher { 
    # Dispatcher is the name of the event-based dispatcher 
    type = Dispatcher 
    # What kind of ExecutionService to use 
    executor = "thread-pool-executor" 
    # Configuration for the thread pool 
    thread-pool-executor { 
    # minimum number of threads to cap factor-based core number to 
    core-pool-size-min = 200 
    # No of core threads ... ceil(available processors * factor) 
    core-pool-size-factor = 20.0 
    # maximum number of threads to cap factor-based number to 
    core-pool-size-max = 200 
    } 
    # Throughput defines the maximum number of messages to be 
    # processed per actor before the thread jumps to the next actor. 
    # Set to 1 for as fair as possible. 
    throughput = 100 
} 

jedoch, wenn ich Apache Bank wie folgt ausführen:

ab -n 200 -c 50 http://LAP:8080/contestroute 

Ergebnisse I Get sind:

Server Software:  Apache-Coyote/1.1 
Server Port:erred:  37500 bytes 
HTML transferred:  10350 bytes 
Requests per second: 4.31 [#/sec] (mean) 
Time per request:  34776.278 [ms] (mean) 
Time per request:  231.842 [ms] (mean, across all concurrent requests) 
Transfer rate:   1.05 [Kbytes/sec] received 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  5 406 1021.3  7 3001 
Processing: 30132 30466 390.8 30308 31231 
Waiting: 30131 30464 391.8 30306 31231 
Total:  30140 30872 998.9 30353 33228   8080 

Document Path:   /contestroute 
Document Length:  69 bytes 

Concurrency Level:  150 
Time taken for tests: 34.776 seconds 
Complete requests:  150 
Failed requests:  0 
Write errors:   0 
Non-2xx responses:  150 
Total transferred:  37500 bytes 
HTML transferred:  10350 bytes 
Requests per second: 4.31 [#/sec] (mean) 
Time per request:  34776.278 [ms] (mean) 
Time per request:  231.842 [ms] (mean, across all concurrent requests) 
Transfer rate:   1.05 [Kbytes/sec] received 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  5 406 1021.3  7 3001 
Processing: 30132 30466 390.8 30308 31231 
Waiting: 30131 30464 391.8 30306 31231 
Total:  30140 30872 998.9 30353 33228 

Fehle ich etwas großes? Was muss ich ändern, damit mein spray und alle Threads nutzt, die ich ihm gegeben habe?

alle Spritzvorgänge und Sperroperationen im gleichen Zusammenhang

+2

Ich weiß, Es ist Beispielcode, aber warum "schlafen" innerhalb des Codes, der als 'Zukunft' ausgeführt wird. Indem du das tust, nimmst du im Grunde genommen den Thread, der diese Zukunft außer Kraft setzt, für 5 Sekunden und es kann während dieser 5 Sekunden keinem anderen Akteur/Zukunft zugewiesen werden. – cmbaxter

+0

ich simuliere eine lange laufende Operation, also kann ich sehen, ob ich 200 Gewindegänge verwenden kann, um diese lang laufende Operation laufen zu lassen. – Jas

+1

Sind Sie sicher, dass das zugrundeliegende Aktorsystem für Spray nicht auch diesen 'async-futures-dispatcher'-Ausführungskontext übernimmt. Wenn es es versehentlich aufhebt (weil es als implizit definiert ist), dann könnte das ein Teil des Problems sein. Es ist eine gute Idee, blockierenden Code in einen separaten Ausführungskontext vom akka main dispatcher zu sperren, wie Sie es hier getan haben, aber es würde seinen Zweck nicht erfüllen, wenn akka diesen Ausführungskontext auch verwendet, um die Aktoren auszuführen, die diese Route darstellen. – cmbaxter

Antwort

13

In Ihrem Beispiel (i renne oben auf Tomcat Servlet 3.0 hinzufügen) passieren. Sie müssen 2 Kontexte teilen:

Auch sehe ich nicht den Grund, dynamisch zu verwenden, ich denke, nur "vollständig" sollte gut sein.

implicit val timeout = 10.seconds 

    // Execution Context for blocking ops 
    val blockingExecutionContext = { 
    ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2000)) 
    } 

    // Execution Context for Spray 
    import context.dispatcher 

    override def receive: Receive = runRoute(contestroute) 

    val contestroute = path("contestroute") { 
    get { 

     complete { 
      Future.apply { 
      val start = System.currentTimeMillis() 
      // np here should be dealt by 200 threads defined below, so why 
      // overall time takes so long? why doesn't it really utilize all 
      // threads I have given to it? how to update the code so it 
      // utilizes the 200 threads? 
      Thread.sleep(5000) 
      val status = s"timediff ${System.currentTimeMillis() - start}ms ${Thread.currentThread().getName}" 
      status 
      }(blockingExecutionContext) 
     } 

    } 
    } 

Danach können Sie es testen mit

ab -n 200 -c 200 http://LAP:8080/contestroute 

und Sie werden feststellen, dass Spray sehen alle 200 Fäden zum Blockieren Operationen erstellen

Ergebnisse:

Concurrency Level:  200 
Time taken for tests: 5.096 seconds 
+1

Kleine Korrektur: Es ist kein Spray, das "alle 200 Threads erzeugt", aber das ist eine Eigenschaft von Scala/Akka. – jrudolph

+2

Das ist ziemlich genau das, was ich in meinem Kommentar gesagt habe, dass Spray auch den Kontext benutzte, in dem die Blockierung stattfand, wodurch der Durchsatz des Systems stark eingeschränkt wurde. – cmbaxter

+0

Bedeutet dieser Ansatz nicht, dass der einzelne Thread immer noch das gesamte Routing verarbeitet (= Engpass + ständige Gefahr, alle Anfragen zu blockieren)? Um eine hohe Parallelität zu erreichen, sollte der Server die Verwaltung der eingehenden Anfrage nicht so schnell wie möglich an einen neuen Thread übergeben? – user48956

Verwandte Themen