Java Asynchrone Verwerking
Voordat we Spring @Async bekijken, zijn de concepten van synchronisatie, asynchroniciteit en multithreading essentieel. We gaan ervan uit dat u bekend bent met deze concepten en zullen de pure Java asynchrone verwerkingsmethode in code bekijken. Als u bekend bent met Java threads, kunt u deze sectie overslaan.
Als we een functie maken die een bericht ontvangt en deze simpelweg afdrukt met behulp van synchrone verwerking, dan kunnen we de code als volgt schrijven. Door dit te veranderen in een multithreading asynchrone methode, kunnen we de broncode als volgt schrijven.
Maar deze methode is erg gevaarlijk omdat we de threads niet kunnen beheren. Als er bijvoorbeeld 10.000 gelijktijdige aanroepen zijn, moeten we in zeer korte tijd 10.000 threads genereren. Het genereren van threads kost veel resources, wat de prestaties van het programma negatief kan beïnvloeden en zelfs kan leiden tot een OOM-fout. Om threads te beheren, moeten we een threadpool implementeren en Java biedt de ExecutorService-klasse hiervoor.
We hebben het totale aantal threads beperkt tot 10 en we kunnen de gewenste multithreading asynchrone verwerking correct uitvoeren. Maar bij deze methode moeten we voor elke methode die we asynchroon willen verwerken de submit()-methode van ExecutorService toepassen, wat leidt tot herhaalde wijzigingen. Met andere woorden, als we een methode willen wijzigen die we aanvankelijk synchroon hebben geschreven naar asynchroon, moeten we de logica van de methode zelf onvermijdelijk wijzigen.
Spring @Async
Een eenvoudige methode
Plaats de @EnableAsync-annotatie boven de Application-klasse en plaats de @Async-annotatie boven de synchrone methoden die u asynchroon wilt verwerken. Maar deze methode heeft het probleem dat threads niet worden beheerd. Dit komt omdat de standaardinstelling van @Async is om SimpleAsyncTaskExecutor te gebruiken, wat geen threadpool is, maar simpelweg threads genereert.
Een methode die een threadpool gebruikt
Verwijder eerst @EnableAsync uit de Application-klasse. Als de Application-klasse @EnableAutoConfiguration of @SpringBootApplication heeft ingesteld, zal de runtime de bean-informatie van threadPoolTaskExecutor lezen uit de SpringAsyncConfig-klasse (die we later zullen maken) die is geconfigureerd met @Configuration.
U kunt de core- en max-grootte instellen. In dit geval verwachten we dat deze met de initiële core-grootte werkt en, als deze niet meer kan verwerken, het aantal threads toeneemt tot de max-grootte, maar dit is niet het geval.
Intern wordt een LinkedBlockingQueue met een grootte van Integer.MAX_VALUE gegenereerd, en als de core-threads de taak niet kunnen verwerken, wacht deze in de wachtrij. Als de wachtrij vol is, worden er threads gegenereerd tot de max-grootte om de taak te verwerken.
Als u het niet prettig vindt om de wachtrijgrootte in te stellen op Integer.MAX_VALUE, kunt u queueCapacity instellen. Met de bovenstaande instelling worden er 3 threads gegenereerd. Als de verwerkingssnelheid vertraagt, worden er 100 taken in de wachtrij geplaatst. Als er meer aanvragen binnenkomen, worden er maximaal 30 threads gegenereerd om de taken te verwerken.
Nadat de threadpool is geconfigureerd, kunt u de naam van de bean toevoegen aan de methode met de @Async-annotatie.
Als u verschillende soorten threadpools wilt instellen, kunt u meerdere bean-generatiemethoden zoals threadPoolTaskExecutor() maken en de gewenste threadpool-bean invoegen wanneer u @Async configureert.
Retourtype en geretourneerde vorm
**Geen retourwaarde**
Dit is het geval wanneer de methode die asynchroon moet worden verwerkt geen verwerkingsresultaten hoeft door te geven. In dit geval kunt u het retourtype van de @Async-annotatie instellen op void.
**Retourwaarde**
U kunt Future, ListenableFuture en CompletableFuture gebruiken als retourtype. De retourvorm van de asynchrone methode kan worden gebundeld met new AsyncResult().
[Future]
future.get() blokkeert en wacht totdat het resultaat van de aanvraag is ontvangen. Dit is een synchrone blokkerende methode en daarom is de performance niet goed. Over het algemeen wordt Future niet gebruikt.
[ListenableFuture]
ListenableFuture kan taken verwerken met behulp van callbacks in een niet-blokkerende modus. De eerste parameter van de addCallback()-methode definieert de callback-methode voor voltooiing van de taak en de tweede parameter definieert de callback-methode voor mislukte taken. Overigens, aangezien het aantal core-threads van de threadpool is ingesteld op 3, kunt u zien dat er aan het begin 3 "Task Start" berichten worden weergegeven.
[CompletableFuture]
Hoewel ListenableFuture op zich al niet-blokkerende logica kan implementeren, kan het, als er een callback in een callback nodig is, leiden tot zeer complexe code, ook wel "callback hell" genoemd.
<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>
Natuurlijk zullen we CompletableFuture in deze sessie niet gedetailleerd behandelen. Als u benieuwd bent naar code die omgaat met complexe callbacks, raadpleeg dan de onderstaande bronlink.
Het is leesbaarder dan het definiëren van callbacks voor ListenableFuture en voert de niet-blokkerende functie volledig uit. Daarom wordt het gebruik van CompletableFuture aanbevolen wanneer u @Async gebruikt en een retourwaarde nodig heeft.
Voordelen van @Async
Ontwikkelaars kunnen methoden synchronously schrijven en als ze asynchrone verwerking nodig hebben, hoeven ze alleen maar de @Async-annotatie boven de methode te plaatsen. Hierdoor kunnen ze code schrijven die goed onderhoudbaar is voor zowel synchrone als asynchrone verwerking.
Waarschuwingen bij @Async
Om de @Async-functie te gebruiken, moet de @EnableAsync-annotatie worden gedeclareerd. Als er geen aparte configuratie is ingesteld, werkt deze in proxymodus. Met andere woorden, alle asynchrone methoden die werken met de @Async-annotatie volgen de beperkingen van Spring AOP. Zie voor meer informatie deze post.
- Zelfs als u @Async aan een private methode toevoegt, werkt AOP niet.
- Als methoden binnen hetzelfde object elkaar aanroepen, werkt AOP niet.
Reacties0