제이온

[Spring] Verwendung von @Async

  • Verfasst in: Koreanisch
  • Land: Alle Ländercountry-flag
  • IT

Erstellt: 2024-04-25

Erstellt: 2024-04-25 22:33

Java asynchrone Verarbeitung

Bevor wir uns Spring @Async ansehen, sind die Konzepte von synchron, asynchron und Multithreading unerlässlich. Wir gehen davon aus, dass Sie mit diesen Konzepten vertraut sind und sehen uns die reine Java-Methode zur asynchronen Verarbeitung anhand von Code an. Wenn Sie mit Java-Threads vertraut sind, können Sie dieses Kapitel überspringen.



Wenn Sie eine Funktion erstellen, die eine Nachricht empfängt und einfach ausgibt, können Sie diese synchron wie im obigen Code schreiben. Wenn Sie dies in eine Multithreading-asynchrone Methode umwandeln, können Sie den Quellcode wie folgt schreiben.



Diese Methode ist jedoch sehr gefährlich, da Threads nicht verwaltet werden können. Wenn beispielsweise 10.000 Aufrufe gleichzeitig erfolgen, müssen in sehr kurzer Zeit 10.000 Threads erstellt werden. Die Kosten für die Thread-Erstellung sind nicht gering, was sich negativ auf die Leistung des Programms auswirken kann und sogar zu einem OOM-Fehler führen kann. Daher muss ein Thread-Pool implementiert werden, um Threads zu verwalten, und Java stellt die ExecutorService-Klasse bereit.



Die Gesamtzahl der Threads ist auf 10 begrenzt, und wir können die gewünschte asynchrone Verarbeitung mit Multithreading korrekt durchführen. Bei dieser Methode muss jedoch die submit()-Methode von ExecutorService für jede Methode angewendet werden, die asynchron verarbeitet werden soll, was zu wiederholten Änderungen führt. Das heißt, wenn Sie eine ursprünglich synchron geschriebene Methode in eine asynchrone Methode ändern möchten, ist eine Änderung der Logik der Methode selbst unvermeidlich.


Spring @Async

Einfache Methode


Fügen Sie einfach die Anmerkung @EnableAsync über der Application-Klasse hinzu und die Anmerkung @Async über der Methode der synchronen Logik, die Sie asynchron verarbeiten möchten. Diese Methode hat jedoch das Problem, dass sie Threads nicht verwaltet. Dies liegt daran, dass die Standardeinstellung von @Async die Verwendung von SimpleAsyncTaskExecutor ist, der kein Thread-Pool ist, sondern einfach Threads erstellt.


Methode mit Thread-Pool

Entfernen Sie zunächst @EnableAsync aus der Application-Klasse. Wenn die Application-Klasse mit @EnableAutoConfiguration oder @SpringBootApplication konfiguriert ist, liest sie zur Laufzeit die Thread-Pool-TaskExecutor-Bean-Informationen aus der SpringAsyncConfig-Klasse (die wir unten erstellen werden). Dies liegt daran, dass @Configuration eingerichtet ist.



Sie können die Core- und Max-Größe festlegen. In diesem Fall wird erwartet, dass die Core-Größe zunächst verwendet wird und die Thread-Anzahl bei Bedarf auf die Max-Größe erhöht wird, wenn mehr Aufgaben verarbeitet werden müssen. Dies ist jedoch nicht der Fall.


Intern wird eine LinkedBlockingQueue mit der Größe Integer.MAX_VALUE erstellt, sodass die Aufgaben, die die Core-Größe überschreiten, in der Warteschlange warten. Wenn die Warteschlange voll ist, werden Threads bis zur Max-Größe erstellt, um die Aufgaben zu verarbeiten.


Wenn Sie es als Belastung empfinden, die Queue-Größe auf Integer.MAX_VALUE festzulegen, können Sie queueCapacity einstellen. Wenn Sie die obige Einstellung verwenden, werden zunächst 3 Threads verwendet. Wenn die Verarbeitungsgeschwindigkeit nachlässt, werden die Aufgaben in eine Warteschlange mit 100 Elementen gestellt. Wenn weitere Anfragen eingehen, werden maximal 30 Threads erstellt, um die Aufgaben zu verarbeiten.


Nachdem die Thread-Pool-Konfiguration abgeschlossen ist, müssen Sie nur noch den Namen der Bean in der Methode mit der @Async-Annotation angeben.



Wenn Sie mehrere Thread-Pool-Typen konfigurieren möchten, erstellen Sie einfach mehrere Bean-Erstellungsmethoden wie threadPoolTaskExecutor() und geben Sie die gewünschte Thread-Pool-Bean bei der @Async-Konfiguration an.


Rückgabetyp-basierte Rückgabeformate

**Kein Rückgabewert**

Dies ist der Fall, wenn die Methode, die asynchron verarbeitet werden soll, keine Verarbeitungsresultate zurückgeben muss. In diesem Fall müssen Sie den Rückgabetyp der @Async-Annotation auf void setzen.


**Rückgabewert vorhanden**

Sie können Future, ListenableFuture und CompletableFuture als Rückgabetypen verwenden. Wenn Sie die Rückgabeform der asynchronen Methode verwenden, können Sie sie in new AsyncResult() einfügen.


[Future]


future.get() wartet blockierend, bis das Anforderungsergebnis eintrifft. Daher wird es zu einer synchronen Blockierungsmethode, was zu einer schlechten Leistung führt. Normalerweise wird Future nicht verwendet.


[ListenableFuture]


Mit ListenableFuture können Sie Aufgaben mithilfe von Callbacks nicht blockierend verarbeiten. Der erste Parameter der Methode addCallback() definiert die Callback-Methode für den Aufgabenabschluss, der zweite Parameter definiert die Callback-Methode für den Aufgabenfehler. Da die Core-Thread-Anzahl des Thread-Pools auf 3 gesetzt ist, können Sie sehen, dass die Nachricht „Task Start“ zuerst dreimal ausgegeben wird.



[CompletableFuture]

Auch mit ListenableFuture allein kann nicht blockierende Logik implementiert werden. Wenn jedoch innerhalb eines Callbacks ein weiterer Callback benötigt wird, führt dies zu sehr komplexem Code, der als Callback-Hölle bezeichnet wird.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9f152db8-c015-43cd-bf13-85c594f5f218%2FUntitled.png?table=block&id=268ac0bc-ca7b-4bcb-b11b-ac611a5038b2&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1455;" width="2000" height="1455"></span>


Natürlich werden wir uns in dieser Lektion nicht eingehender mit CompletableFuture befassen. Wenn Sie also an Code interessiert sind, der komplexe Callbacks behandelt, konsultieren Sie bitte den unten angegebenen Link.



Die Lesbarkeit ist besser als bei der Definition von Callbacks in ListenableFuture, und die nicht blockierende Funktion wird perfekt ausgeführt. Daher wird empfohlen, CompletableFuture zu verwenden, wenn Sie @Async verwenden und einen Rückgabewert benötigen.


Vorteile von @Async

Entwickler können Methoden synchron schreiben und, wenn sie asynchron sein sollen, einfach die Annotation @Async über die Methode setzen. Daher können Sie gut wartbaren Code erstellen, der sowohl synchron als auch asynchron ist.


Vorsichtsmaßnahmen bei @Async

Um die @Async-Funktion nutzen zu können, müssen Sie die Annotation @EnableAsync deklarieren. Wenn Sie dabei keine besonderen Einstellungen vornehmen, arbeitet sie im Proxy-Modus. Das heißt, alle asynchronen Methoden, die mit der @Async-Annotation arbeiten, folgen den Einschränkungen von Spring AOP. Weitere Informationen finden Sie unterdiesem Beitrag.


  • Wenn Sie @Async an eine private Methode anfügen, funktioniert AOP nicht.
  • Wenn Methoden innerhalb desselben Objekts aufgerufen werden, funktioniert AOP nicht.


Quellen

Kommentare0