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
.
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.
Spring Dependencies
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
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")
}
}
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()
}
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.
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?
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 |
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")
}
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
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é.
Recommended Posts