[JAVA] Spring avec Kotorin --6 Traitement asynchrone

Aperçu / Description

Dans le passé, le comportement synchrone de l'exécution et de la réception des résultats du traitement des demandes était suffisant dans de nombreux endroits. Cependant, ces dernières années, avec les progrès des appareils mobiles tels que les smartphones et les processeurs multicœurs, Un traitement asynchrone qui peut effectuer plusieurs traitements en même temps est également requis.

Dans Spring, vous pouvez facilement effectuer un traitement asynchrone sur un autre thread en utilisant l'annotation @ Async. Voyons comment créer un traitement asynchrone en utilisant @ Async.

Traitement asynchrone

Lorsque vous exécutez une instruction en Java, le traitement est effectué dans le thread. Dans le cas d'un thread unique, les instructions sont traitées séquentiellement sur le thread. Par conséquent, un traitement simultané n'est pas possible.

Par conséquent, préparez plusieurs threads et exécutez des instructions sur chaque thread. Exécutez l'instruction dans un autre thread sans attendre la fin pour réaliser un traitement parallèle de manière asynchrone.

Threads.png

Hypothèse / environnement

Version d'exécution

Spring Dependencies

Environnement de développement

Procédure / Explication

Traitement asynchrone simple

Activer le traitement asynchrone

Définissez les paramètres pour activer le traitement asynchrone dans l'application Spring Boot. Tout ce que vous avez à faire est d'ajouter l'annotation @ EnableAsync à la classe de configuration (annotation @ Configuration ou la classe avec l'annotation @ SpringBootApplication).

@SpringBootApplication
@EnableAsync
class SimpleApplication

Implémentation du traitement asynchrone

Ajoutez l'annotation @ Async à la fonction pour laquelle vous souhaitez effectuer un traitement asynchrone dans un autre thread. La fonction avec cette annotation @ Async est traitée comme la fonction à traiter de manière asynchrone, et l'instruction traitée est exécutée dans un autre thread.

import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit

@Service("Async Task Service")
class AsyncTaskService {

    val logger = LoggerFactory.getLogger(this::class.java.name)

    @Async
    fun standardTask() {
        logger.info("Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Task End")
    }
}

Appel de traitement asynchrone

Placez un contrôleur REST qui appelle la classe responsable du traitement asynchrone avec l'annotation @ Async. Définissez le point de terminaison d'accès sur / async. Cela permet à ce processus asynchrone d'être appelé à http: // localhost: 8080 / async.

@RestController
@RequestMapping("/async")
class AsyncTaskController {

    @GetMapping
    fun callStandardTask() = service.standardTask()
}

Exécution du traitement asynchrone

Appelons en fait le traitement asynchrone 5 fois de suite.

$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async

Le journal d'exécution du début et de la fin de chaque processus asynchrone est affiché dans la sortie standard comme indiqué ci-dessous.

2018-12-19 20:50:22.662  INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:00.545  INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:01.886  INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:04.397  INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:05.550  INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:06.887  INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:51:09.400  INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:03.338  INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:05.123  INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:06.290  INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService    : Normal Task End

En regardant ce résultat, il peut être confirmé que 5 threads de cTaskExecutor-1 à cTaskExecutor-5 sont créés et traités, et chacun est traité de manière asynchrone.

Traitement asynchrone à l'aide du pool de threads

Dans le processus asynchrone exécuté précédemment, des threads ont été créés pour le nombre d'appels. Et si cette application est appelée très souvent? Il est possible qu'un grand nombre de threads soit créé, épuisant les ressources système, réduisant les performances et, dans le pire des cas, arrêtant le système. Il existe de nombreux serveurs d'applications Java EE qui permettent de contrôler le nombre de threads en tant que plate-forme. Mais qu'en est-il d'une application Spring Boot qui n'utilise pas un serveur d'applications Java EE comme celui-ci?

Définition de ThreadPoolTaskExecutor

Lors de l'exécution d'un traitement asynchrone dans Spring, en ajoutant l'annotation @ Async, SimpleAsyncTaskExecutor est utilisé par défaut pour créer un autre thread et effectuer un traitement asynchrone.

D'autre part, si vous voulez configurer le pool de threads pour contrôler le nombre de threads, configurez et utilisez ThreadPoolTaskExecutor. Définissez dans la classe de configuration avec @ EnableAsync.

Ci-dessous, nous créons deux types de piscines.

@SpringBootApplication
@EnableAsync
class SimpleApplication {

    @Bean
    fun normalTaskExecutor(): TaskExecutor  = ThreadPoolTaskExecutor().apply {
        corePoolSize = 1
        setQueueCapacity(5)
        maxPoolSize = 1
        setThreadNamePrefix("NormalThread-")
        setWaitForTasksToCompleteOnShutdown(true)
    }

    @Bean
    fun prioritizedTaskExecutor(): TaskExecutor  = ThreadPoolTaskExecutor().apply {
        corePoolSize = 5
        setQueueCapacity(5)
        maxPoolSize = 5
        setThreadNamePrefix("PrioritizedThread-")
        setWaitForTasksToCompleteOnShutdown(true)
    }
}

Définissez les attributs suivants pour configurer le pool de threads.

Nom Contenu
corePoolSize Créez le nombre de threads jusqu'à cette valeur de paramètre
setQueueCapacity Lorsque le nombre de corePoolSize est dépassé, mise en file d'attente jusqu'à cette valeur de paramètre
maxPoolSize Lors de la mise en file d'attente au maximum de setQueueCapacity, le nombre de threads est créé jusqu'à cette valeur de paramètre

ThreadPool.png

Implémentation du traitement asynchrone avec ThreadPoolTaskExecutor spécifié

Spécifiez explicitement le nom du bean de ThreadPoolTaskExecutor avec l'annotation @ Async comme indiqué ci-dessous. Détermine quel ThreadPoolTaskExecutor sera asynchrone.

    @Async("prioritizedTaskExecutor")
    fun prioritizedTask() {
        logger.info("Prioritized Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Prioritized Task End")
    }

    @Async("normalTaskExecutor")
    fun normalTask() {
        logger.info("Normal Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Normal Task End")
    }

Exécution du traitement asynchrone

Regardons le résultat du traitement asynchrone avec le nombre de pools de threads défini sur «1».

$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal

Comme indiqué ci-dessous, vous pouvez voir que le traitement est effectué uniquement par le thread NormalThread-1. Comme spécifié, il est traité avec le nombre de threads «1».

2018-12-19 22:30:17.654  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:22.658  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:24.437  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:29.441  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:29.441  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:34.442  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:34.443  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:39.445  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:39.446  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:44.455  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End

Ensuite, regardons le traitement asynchrone avec le nombre de pools de threads défini sur «5».

$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high

Ici, vous pouvez voir que 5 threads de HighThread-1 à HighThread-5 ont été créés et utilisés. Comme vous pouvez le voir, vous pouvez facilement effectuer un traitement asynchrone compte tenu du contrôle du pool de threads.

2018-12-19 22:37:55.784  INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:57.469  INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:57.898  INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:58.956  INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:59.582  INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:38:00.787  INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:02.473  INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:02.900  INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:03.957  INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:04.586  INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End

Résumé / Rétrospective

Le traitement asynchrone est un traitement qui peut être utilisé efficacement non seulement dans les applications Web, mais également dans les applications par lots. De cette manière, le traitement asynchrone peut être effectué tout en créant facilement des threads et en contrôlant le pool de threads, il semble donc qu'il existe de nombreuses situations possibles dans lesquelles il peut être utilisé.

Cette source

Recommended Posts

Spring avec Kotorin --6 Traitement asynchrone
Ressort avec actionneur Kotorin --5
Traitement asynchrone avec Spring Boot en utilisant @Async
Printemps avec Kotorin ―― 1. INITIALISATION PRINTEMPS
Traitement asynchrone avec exécution régulière dans Spring Boot
Spring with Kotorin --- 8. Couche de référentiel
Ressort avec Kotorin ―― 7. Couche de service
Spring with Kotorin ―― 9. Migration de base de données ―― Flyway
Traitement lors du démarrage d'une application avec Spring Boot
Spring avec Kotorin --2 RestController et Data Class
Noël avec traitement
Spring with Kotorin --8 Repository layer --Supplément: H2 Database
Validation personnalisée avec Spring
Avec Kotorin ―― 7. Fonction de cadrage
Télécharger avec Spring Boot
Exécutez un traitement arbitraire après l'authentification de base avec Spring Boot.
Ressort avec Kotorin ―― 3. Omettre les crochets d'onde de la fonction
Contrôlez le flux de traitement Spring Batch avec JavaConfig.
Premiers pas avec le traitement Doma-Annotation
Générer un code à barres avec Spring Boot
Hello World avec Spring Boot
Configuration Java avec Spring MVC
Implémenter GraphQL avec Spring Boot
Démarrez avec Spring Boot
Exécutez LIFF avec Spring Boot
Connexion SNS avec Spring Boot
Téléchargement de fichiers avec Spring Boot
Spring Boot commençant par copie
Fonction de connexion avec Spring Security
Utilisation de Mapper avec Java (Spring)
Spring Boot à partir de Docker
Hello World avec Spring Boot
Définir des cookies avec Spring Boot
Utiliser Spring JDBC avec Spring Boot
Ajouter un module avec Spring Boot
Premiers pas avec Spring Boot
Lier l'API avec Spring + Vue.js
[Swift] Traitement asynchrone à l'aide de PromiseKit
Créer un micro service avec Spring Boot
Diapositive de présentation réalisée avec Processing
Envoyer du courrier avec Spring Boot
Je souhaite effectuer un traitement asynchrone et une exécution périodique avec Rail !!!
Mise en œuvre de la fonction d'authentification avec Spring Security ②
Essayez d'implémenter le traitement asynchrone dans Azure
Implémentez la fonction d'authentification avec Spring Security ③
Créez une application avec Spring Boot 2
Liaison de base de données avec doma2 (Spring boot)
Implémenter le traitement déclaratif des tentatives à l'aide de Spring Retry
Programmation Spring Boot avec VS Code
Jusqu'à "Hello World" avec Spring Boot
Créer une application d'enquête avec Spring Boot
NLP4J [006-031] Le traitement de 100 langues frappe avec le verbe NLP4J # 31
Mise en œuvre de la fonction d'authentification avec Spring Security ①
Vitesse de traitement avec et sans statique
Obtenez des résultats de validation avec Spring Boot
Implémentation du traitement asynchrone dans Tomcat
Découvrez l'architecture de traitement de l'authentification Spring Security
Implémenter le téléchargement de fichiers avec Spring MVC