[JAVA] Asynchronous processing with Spring Boot using @Async

About this document

Since I investigated what is necessary when performing asynchronous processing with SpringBoot, in this document,

  1. Asynchronous processing method using @Async
  2. Setting up threads using TaskExcecuter

I summarized about.

Execution environment

java8 + Spring Boot 2.1.0 + lombok

build.gradle is as follows


buildscript {
    ext {
        springBootVersion = '2.1.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    compileOnly('org.projectlombok:lombok')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

Try

Implement the process you want to execute asynchronously

Implementation

Create a process that just sleeps properly

@Slf4j
@Service
public class AsyncService {

    @Async("Thread2")
    public CompletableFuture<String> findName(String name, Long sleepTime) throws InterruptedException {
        log.warn("Async function started. processName: " + name + "sleepTime: " + sleepTime);
        Thread.sleep(sleepTime);
        
        //Use the return value you actually want to use in the Completable Future so that you can handle asynchronous processing.
        return CompletableFuture.completedFuture(name);
    }

}
Commentary

Implement the process of calling and handling the above logic

Implementation

Implement an appropriate Controller to handle asynchronous processing


@Slf4j
@RequiredArgsConstructor
@RestController
public class FindNameContoller {

    private final AsyncService asyncService;

    @GetMapping("/users/")
    public List<String> findUsers() throws Exception {
        long start = System.currentTimeMillis();
        long heavyProcessTime = 3000L;
        long lightProcessTime = 1000L;

        log.warn("request started");
        CompletableFuture<String> heavyProcess = asyncService.findName("heavy", heavyProcessTime);
        CompletableFuture<String> lightProcess = asyncService.findName("light", lightProcessTime);

        //Process to be executed when heavyProcess is finished
        heavyProcess.thenAcceptAsync(heavyProcessResult -> {
            log.warn("finished name=" + heavyProcessResult + ", sleep = " + heavyProcessTime);
        });

        //Process to be executed when lightProcess is finished
        lightProcess.thenAcceptAsync(lightProcessResult -> {
            log.warn("finished name=" + lightProcessResult + ", sleep = " + lightProcessTime);
        });

        //When the specified process is completed, the subsequent processes will be executed.
        CompletableFuture.allOf(heavyProcess, lightProcess).join();

        //Creating a return value
        List<String> names = new ArrayList<>();
        names.add(heavyProcess.get());
        names.add(lightProcess.get());

        Thread.sleep(10L);

        long end = System.currentTimeMillis();
        //Output the time of the entire process
        log.warn("request finished. time: " + ((end - start))  + "ms");

        return names;
    }

}
Commentary

Set the upper limit of Thread, etc.

Implementation

Describe various settings in the main Application class


@EnableAsync
@SpringBootApplication
public class AsyncTrainingApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsyncTrainingApplication.class, args);
    }

    @Bean("Thread1") //This setting is not specified and is not used
    public Executor taskExecutor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);
        executor.setThreadNamePrefix("Thread1--");
        executor.initialize();
        return executor;
    }

    @Bean("Thread2") //Set here"Thread2"Is set to @Async and that setting is used
    public Executor taskExecutor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); //The default Thread size. When it overflows, it queues up to the size of Queue Capacity
        executor.setQueueCapacity(1); //The size of the waiting queue. Increase Thread to MaxPoolSize when overflowing
        executor.setMaxPoolSize(500); //Setting how much Thread to increase. If it overflows from this value, the process will be rejected and an Exception will occur.
        executor.setThreadNamePrefix("Thread2--");
        executor.initialize();
        return executor;
    }

    @Bean("Reject") //With this setting, queuing cannot be performed, Thread cannot be increased, and a RejectedExecutionException occurs.
    public Executor rejectTaskExecuter() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setQueueCapacity(0);
        executor.setMaxPoolSize(1);
        executor.setThreadNamePrefix("Reject--");
        executor.initialize();
        return executor;
    }

}
Commentary

Execute

When you execute the process, the following log appears and you can see that it is running asynchronously

2018-11-27 20:19:13.181  WARN 8711 --- [nio-8080-exec-5] c.e.asynctraining.FindNameContoller      : request started
2018-11-27 20:19:13.181  WARN 8711 --- [TaskExecutor-31] com.example.asynctraining.AsyncService   : Async function started. processName: heavysleepTime: 3000
2018-11-27 20:19:13.182  WARN 8711 --- [TaskExecutor-32] com.example.asynctraining.AsyncService   : Async function started. processName: lightsleepTime: 1000
2018-11-27 20:19:14.187  WARN 8711 --- [onPool-worker-2] c.e.asynctraining.FindNameContoller      : finished name=light, sleep = 1000
2018-11-27 20:19:16.182  WARN 8711 --- [onPool-worker-2] c.e.asynctraining.FindNameContoller      : finished name=heavy, sleep = 3000
2018-11-27 20:19:16.194  WARN 8711 --- [nio-8080-exec-5] c.e.asynctraining.FindNameContoller      : request finished. time: 3013ms

Rough summary

Impressions

Referenced materials

Recommended Posts

Asynchronous processing with Spring Boot using @Async
Asynchronous processing with regular execution in Spring Boot
Spring with Kotorin --6 Asynchronous processing
Try using Spring Boot with VS Code
Download with Spring Boot
Execute arbitrary processing after Basic authentication with Spring boot.
Try using DI container with Laravel and Spring Boot
Generate barcode with Spring Boot
Hello World with Spring Boot
Implement GraphQL with Spring Boot
Try using OpenID Connect with Keycloak (Spring Boot application)
Get started with Spring boot
[JUnit 5 compatible] Write a test using JUnit 5 with Spring boot 2.2, 2.3
Hello World with Spring Boot!
File upload with Spring Boot
Asynchronous processing with Shoryuken + SQS
Spring Boot starting with copy
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
Try using Spring Boot Security
[Swift] Asynchronous processing using PromiseKit
Create microservices with Spring Boot
Send email with spring boot
Execution of initial processing using Spring Boot Command Line Runner
I tried to get started with Swagger using Spring Boot
Use Basic Authentication with Spring Boot
gRPC on Spring Boot with grpc-spring-boot-starter
Create an app with Spring Boot 2
Hot deploy with Spring Boot development
Database linkage with doma2 (Spring boot)
Spring Boot Tutorial Using Spring Security Authentication
Spring Boot programming with VS Code
Until "Hello World" with Spring Boot
Inquiry application creation with Spring Boot
Get validation results with Spring Boot
(Intellij) Hello World with Spring Boot
Create an app with Spring Boot
Google Cloud Platform with Spring Boot 2.0.0
Check date correlation with Spring Boot
I tried GraphQL with Spring Boot
[Java] LINE integration with Spring Boot
Beginning with Spring Boot 0. Use Spring CLI
I tried Flyway with Spring Boot
Message cooperation started with Spring Boot
Spring Boot gradle build with Docker
Part 1: Try using OAuth 2.0 Login supported by Spring Security 5 with Spring Boot
Image Spring Boot app using jib-maven-plugin and start it with Docker
Hello World with Eclipse + Spring Boot + Maven
Send regular notifications with LineNotify + Spring Boot
HTTPS with Spring Boot and Let's Encrypt
Start web application development with Spring Boot
Launch Nginx + Spring Boot application with docker-compose
I tried Lazy Initialization with Spring Boot 2.2.0
Implement CRUD with Spring Boot + Thymeleaf + MySQL
Implement paging function with Spring Boot + Thymeleaf
(IntelliJ + gradle) Hello World with Spring Boot