2017-03-20 5 views
0

Ich versuche, die Vorteile von Async in Java zu verstehen.Java - Async - Thread-Pool

Szenario 1: I eine Federverschluß-Webanwendung tomcat bereitgestellt habe, mit tomcat min und max Fäden beide auf 200.

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    @RequestMapping("/test") 
    public String test(){ 
     return service.execute(); 
    } 
} 

Szenario 2: Ich habe eine Federverschluß-Webanwendung tomcat entfalteten die Gesamtzahl der Threads mit tomcat min und max Fäden beide auf 100

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    private ExecutorService executorService = Executors.newFixedThreadPool(100); 

    @RequestMapping("/test") 
    public DeferredResult<String> test(){ 
     DeferredResult<String> deferredResult = new DeferredResult<>(); 
     CompletableFuture.supplyAsync(service::execute, executorService). 
      whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));  
     return deferredResult;  
    } 
} 

In jeder Szenarien ist 200.

Aber ich sehe nicht, wie scen Ario 2 wird besser funktionieren:

In Szenario 1, wenn 400 Anfragen zur gleichen Zeit eingehen, werden die ersten 200 von den 200 HTTP-Threads bedient und die nächsten 200 müssen 3 Sekunden warten (plus ein bisschen) bis einer der Threads wieder verfügbar wird.

So war der Durchsatz 400 Anfragen pro 6 Sekunden = 66,6 Anfragen pro Sekunde.

durchschnittliche Antwortzeit war (200 * 200 * 3 + 6)/(400) = 4,5 Sekunden

In Szenario 2 wenn 400 Anfragen zur gleichen Zeit kommen. Die ersten 100 werden sofort von den 100 http-Threads bedient, wobei jeder dieser Threads den Dienst aufruft, nicht auf das Ergebnis wartet und dann sofort wieder aufnimmt und verfügbar wird, um die nächsten 100 Anfragen zu bedienen. Aber jetzt für die zweiten 100 Anfragen, wenn jeder der http-Threads den Dienst aufruft, wartet dieser Dienst momentan 3 Sekunden (minus ein bisschen), um die Verarbeitung der ersten 100 Threads zu beenden. Die nächsten 100 werden in die Warteschlange gestellt (im Threadpool des Executorservice). So haben wir in kürzester Zeit alle 400 Anfragen bearbeitet, aber 100 werden im Service bearbeitet (3 Sekunden warten), während 300 im Executor Service Thread Pool in der Warteschlange stehen. 3 Sekunden später werden die ersten 100 fertiggestellt, die nächsten 100 aus der Warteschlange genommen und verarbeitet und so weiter.

also der Durchsatz beträgt 400 Anfragen in 12 Sekunden = 33,3 Anfragen pro Sekunde

durchschnittliche Antwortzeit war (100 * 3 + 100 * 6 + 100 * 9 + 100 * 12)/(400) = 7,5 Sekunden

Nun könnte jemand argumentieren: "Ich kann Szenario 2 verbessern, indem ich die Anzahl der Threads im Executor-Service-Thread-Pool erhöhe", auf die ich antworten könnte: "Gut, dann kann ich die Anzahl der Threads in Tomcat erhöhen Pool in Szenario 1 um den gleichen Betrag '

Antwort

0

Um die Vorteile von async in diesem Szenario zu sehen, müssten Sie mak e Ihren Service auch asynchron. Anstatt einen Schlaf zu machen, der den Thread bindet, kehrt er sofort zurück, nachdem er etwas geplant hat, das drei Sekunden später ausgeführt werden soll. In diesem Fall wären alle Anfragen in etwas mehr als drei Sekunden abgeschlossen. Ihr Durchsatz wäre 133 Anfragen pro Sekunde und die durchschnittliche Antwortzeit würde drei Sekunden betragen. Und Sie hätten im Wesentlichen die gleiche Reaktionszeit, wenn Sie die Anzahl der Threads verringert hätten. Der Punkt von async ist, dass Ihre Threads, die im Leerlauf auf I/O warten, sofort frei sind, etwas anderes zu tun, so dass Sie nicht so viele Threads verwenden müssen, die eine teure Ressource sind, um Ihre Arbeitslast zu erfüllen .

+0

Danke für die Antwort. Wenn der Dienst jedoch auch async ist und sofort zurückkehrt, welcher Thread auf die Antwort wartet, die die ausführbare Zukunft abschließt –

+0

Der Aufrufer der API wartet immer noch auf die Antwort 'OK'. Ein Thread muss diese Antwort zurückgeben, auch wenn es sich nicht um einen Tomcat-HTTP-Thread handelt. –

+0

@ jonathan.stiles - Wenn die Zeit für eine Zeitgeberaufgabe verstrichen ist, wird die Aufgabe einem inaktiven Thread im Threadpool zugewiesen, oder wenn kein inaktiver Thread vorhanden ist, wird sie einer Warteschlange zugewiesen, die beim nächsten Thread in der Warteschlange bedient wird Pool wird inaktiv. Dieser Thread aus dem Thread-Pool vervollständigt schließlich das DeferredResult und sendet die Antwort an den Client. In ordnungsgemäßem async werden alle Threads in dem Thread-Pool, der darauf wartet, geplant zu werden, entweder erledigt oder sind inaktiv. – antlersoft

0

Sie haben eine sehr synthetische Situation in Ihrer Frage.

Nehmen wir an, Sie haben 10 Threads in beiden (10 HTTP-Threads und 5 + 5 Threads für die async-Version), und Ihr Programm beinhaltet mehr als den Aufruf einer Methode, die schläft. 80% Ihrer Anfragen beinhalten jedoch eine Operation, die 3 Sekunden dauert (beispielsweise eine Datenbankabfrage).

Jetzt ist es in beiden Fällen passiert, dass es Ihnen gelungen ist, alle Ihre Threads gleichzeitig die Blockierungsmethode aufzurufen. Bis jetzt gibt es keinen großen Unterschied. Wenn ein anderer Anruf zur Blockierungsmethode kommt, muss er warten.

Nun, plötzlich erhalten Sie eine Anfrage für eine andere Operation, sagen wir login. Die Anmeldung ist einfach und es wird nur nach einer Zeile in der Datenbank gesucht. Im ersten Fall muss es 3 Sekunden warten, da keine verfügbaren HTTP-Threads verfügbar sind. Im zweiten Fall haben Sie einen völlig unabhängigen Threadpool, der voll ist, aber da Sie ihn nicht für die Anmeldung verwenden, erhalten Sie sofort Ihre Login-Anfrage.

Okay, also warum nicht eine Größe 1000 Threadpool ohne DeferredResult erstellen? Threads sind teuer. Sie wollen nicht in eine Situation geraten, in der es Ihnen gelungen ist, 1000 Threads zu bekommen, die eine teure Aufgabe ausführen, Ihre CPU ist auf 100% und statt 3 Sekunden wird die Laufzeit 30 Sekunden für jede Anfrage. Ihr Server wird gedrosselt und der Durchsatz geht auf 0.

Dies gilt auch für Verbindungspools. Weniger ist mehr.

+0

Ich denke, das ist vielleicht kein fairer Vergleich zu sagen, "10 Threads in beiden" Wenn jeder 10 http-Threads hat, aber Szenario zwei hat auch einen Executorservice, um den asynchronen Dienst zu behandeln, dann hat Szenario zwei tatsächlich mehr Threads. –

+0

Es spielt keine Rolle. Einer hat X-Allzweck-Threads für alle Anfragen, der andere hat Y-Allzweck-Threads und Z-Threads für bestimmte lange laufende Operationen (wobei Y + Z = X). Die zweite ermöglicht einen besseren Durchsatz. Dein Beispiel ist einfach so schlecht (und völlig unabhängig von der Realität), dass es dich verwirrt. Der Vorteil liegt darin, dass * Ihre Anforderungen nicht asynchron sind, sondern Sie selbst für diejenigen verwaltet werden, die viel Zeit mit Warten verbringen. – Kayaman