Spring with Kotorin --6 Asynchronous processing

Overview / Description

In the past, application processing was often sufficient in a synchronous way of executing and receiving results. However, in recent years, with the progress of mobile devices such as smartphones and multi-core processors, Asynchronous processing that can perform multiple processing at the same time is also required.

In Spring, you can easily perform asynchronous processing on another thread by using the @ Async annotation. Let's check how to create asynchronous processing using @ Async.

Asynchronous processing

When you execute any instruction in Java, the processing is done in a thread. In the case of single thread, the instructions are processed sequentially on the thread. Therefore, simultaneous processing is not possible.

Therefore, prepare multiple threads and execute instructions on each thread. Asynchronous parallel processing is realized by executing instructions in another thread without waiting for the end.

Threads.png

Assumptions / Environment

Runtime version

Spring Dependencies

Development environment

Procedure / Explanation

Simple asynchronous processing

Enable asynchronous processing

Set to enable asynchronous processing in Spring Boot application. All you have to do is add the @EnableAsync annotation to the configuration class (@Configuration annotation or the class with the @ SpringBootApplication annotation).

@SpringBootApplication
@EnableAsync
class SimpleApplication

Implementation of asynchronous processing

Annotate the function that you want to perform asynchronous processing in another thread with @Async. The function with this @ Async annotation is treated as the function to be processed asynchronously, and the processed instruction is executed in another 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")
    }
}

Asynchronous processing call

Place a REST controller that calls the class responsible for asynchronous processing with the @ Async annotation. Set the access endpoint as / async. This allows this asynchronous process to be called at http: // localhost: 8080 / async.

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

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

Execution of asynchronous processing

Let's actually call asynchronous processing 5 times in a row.

$ 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

The run-time log of the start and end of each asynchronous process is displayed on the standard output as shown below.

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

Looking at this result, you can see that 5 threads from cTaskExecutor-1 to cTaskExecutor-5 are created and processed, and each is processed asynchronously.

Asynchronous processing using thread pool

In the asynchronous process executed earlier, threads were created for the number of calls. What if this is an application that is called very often? It is possible that a large number of threads will be created, exhausting system resources, reducing performance, and in the worst case, system down. There are many Java EE application servers that provide the ability to control the number of threads as a platform. So what do you do with a Spring Boot application that doesn't use a Java EE application server like this one?

Definition of ThreadPoolTaskExecutor

When performing asynchronous processing in Spring, by adding @ Async annotation, SimpleAsyncTaskExecutor is used by default to create another thread and asynchronous processing is performed.

On the other hand, if you want to configure the thread pool to control the number of threads, configure and use ThreadPoolTaskExecutor. Define it in the configuration class with @EnableAsync.

Below, we are creating two types of pools.

@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)
    }
}

Set the following attributes to configure the thread pool.

name Contents
corePoolSize Create the number of threads up to this setting value
setQueueCapacity When the number of corePoolSize is exceeded, queuing up to this setting value
maxPoolSize When queuing to the maximum of setQueueCapacity, the number of threads is created up to this set value.

ThreadPool.png

Implementation of asynchronous processing with ThreadPoolTaskExecutor specified

Explicitly specify the bean name of ThreadPoolTaskExecutor with the @Async annotation as shown below. Determines which ThreadPoolTaskExecutor will be asynchronous.

    @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")
    }

Execution of asynchronous processing

Let's look at the result of asynchronous processing with the number of thread pools set to 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

As shown below, you can see that the processing is performed only by the NormalThread-1 thread. As specified, it is processed with the number of 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

Next, let's look at asynchronous processing that sets the number of thread pools to 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

Here you can see that 5 threads from HighThread-1 to HighThread-5 have been created and used. As you can see, you can easily perform asynchronous processing considering the control of the thread pool.

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

Summary / Looking back

Asynchronous processing is a processing that can be effectively used not only in Web applications but also in batch applications. Since asynchronous processing can be performed while easily creating threads and controlling the thread pool in this way, it seems that there are many possible situations in which it can be used.

This source

Recommended Posts

Spring with Kotorin --6 Asynchronous processing
Spring with Kotorin --- 5. Actuator
Asynchronous processing with Spring Boot using @Async
Spring with Kotorin ―― 1. SPRING INITIALIZR
Asynchronous processing with regular execution in Spring Boot
Spring with Kotorin --8 Repository layer
Asynchronous processing with Shoryuken + SQS
Spring with Kotorin ―― 7. Service layer
Spring with Kotorin --9 Database migration --Flyway
Processing at application startup with Spring Boot
Spring with Kotorin --2 RestController and Data Class
Christmas with Processing
Spring with Kotorin --8 Repository layer --Supplement: H2 Database
Self-made Validation with Spring
With Kotorin ―― 7. Scoping Function
Download with Spring Boot
Execute arbitrary processing after Basic authentication with Spring boot.
Spring with Kotorin --3. Omitting curly braces from the function
Control the processing flow of Spring Batch with JavaConfig.
Getting Started with Doma-Annotation Processing
Generate barcode with Spring Boot
Hello World with Spring Boot
Java Config with Spring MVC
Implement GraphQL with Spring Boot
Get started with Spring boot
[Swift] About asynchronous processing "Operation"
Run LIFF with Spring Boot
SNS login with Spring Boot
[Swift] What is asynchronous processing?
File upload with Spring Boot
Spring Boot starting with copy
Login function with Spring Security
Using Mapper with Java (Spring)
Spring Boot starting with Docker
Hello World with Spring Boot
Set cookies with Spring Boot
Use Spring JDBC with Spring Boot
Add module with Spring Boot
Getting Started with Spring Boot
Link API with Spring + Vue.js
[Swift] Asynchronous processing using PromiseKit
Create microservices with Spring Boot
Presentation slides made with Processing
Send email with spring boot
I want to perform asynchronous processing and periodic execution with Rail !!!
Implemented authentication function with Spring Security ②
Try implementing asynchronous processing in Azure
Implemented authentication function with Spring Security ③
Create an app with Spring Boot 2
Database linkage with doma2 (Spring boot)
Implement declarative retry processing using Spring Retry
Spring Boot programming with VS Code
Until "Hello World" with Spring Boot
Inquiry application creation with Spring Boot
NLP4J [006-031] 100 language processing knocks with NLP4J # 31 verb
Implemented authentication function with Spring Security ①
Processing speed with and without static
Get validation results with Spring Boot
Implementation of asynchronous processing in Tomcat
Learn Spring Security authentication processing architecture
Implement file download with Spring MVC